Ignore chains and states default directories.
[rhynodge.git] / src / main / java / net / pterodactylus / reactor / loader / ReactionLoader.java
1 /*
2  * Reactor - 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.reactor.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.reactor.Action;
27 import net.pterodactylus.reactor.Filter;
28 import net.pterodactylus.reactor.Query;
29 import net.pterodactylus.reactor.Reaction;
30 import net.pterodactylus.reactor.Trigger;
31 import net.pterodactylus.reactor.loader.Chain.Parameter;
32 import net.pterodactylus.reactor.loader.Chain.Part;
33
34 /**
35  * Creates {@link Reaction}s from {@link Chain}s.
36  *
37  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
38  */
39 public class ReactionLoader {
40
41         /**
42          * Creates a {@link Reaction} from the given {@link Chain}.
43          *
44          * @param chain
45          *            The chain to create a reaction from
46          * @return The created reaction
47          * @throws LoaderException
48          *             if a class can not be loaded
49          */
50         @SuppressWarnings("static-method")
51         public Reaction loadReaction(Chain chain) throws LoaderException {
52
53                 /* check if chain is enabled. */
54                 if (!chain.enabled()) {
55                         throw new IllegalArgumentException("Chain is not enabled.");
56                 }
57
58                 /* create query. */
59                 Query query = createObject(chain.query().name(), "net.pterodactylus.reactor.queries", extractParameters(chain.query().parameters()));
60
61                 /* create filters. */
62                 List<Filter> filters = new ArrayList<Filter>();
63                 for (Part filterPart : chain.filters()) {
64                         filters.add(ReactionLoader.<Filter> createObject(filterPart.name(), "net.pterodactylus.reactor.filters", extractParameters(filterPart.parameters())));
65                 }
66
67                 /* create trigger. */
68                 Trigger trigger = createObject(chain.trigger().name(), "net.pterodactylus.reactor.triggers", extractParameters(chain.trigger().parameters()));
69
70                 /* create action. */
71                 Action action = createObject(chain.action().name(), "net.pterodactylus.reactor.actions", extractParameters(chain.action().parameters()));
72
73                 return new Reaction(chain.name(), query, filters, trigger, action).setUpdateInterval(TimeUnit.SECONDS.toMillis(chain.updateInterval()));
74         }
75
76         //
77         // STATIC METHODS
78         //
79
80         /**
81          * Extracts all parameter values from the given parameters.
82          *
83          * @param parameters
84          *            The parameters to extract the values from
85          * @return The extracted values
86          */
87         private static List<String> extractParameters(List<Parameter> parameters) {
88                 List<String> parameterValues = new ArrayList<String>();
89
90                 for (Parameter parameter : parameters) {
91                         parameterValues.add(parameter.value());
92                 }
93
94                 return parameterValues;
95         }
96
97         /**
98          * Creates a new object.
99          * <p>
100          * First, {@code className} is used to try to load a {@link Class} with that
101          * name. If that fails, {@code packageName} is prepended to the class name.
102          * If no class can be found, a {@link LoaderException} will be thrown.
103          * <p>
104          * If a class could be located using the described method, a constructor
105          * will be searched that has the same number of {@link String} parameters as
106          * the given parameters. The parameters from the given parameters are then
107          * used in a constructor call to create the new object.
108          *
109          * @param className
110          *            The name of the class
111          * @param packageName
112          *            The optional name of the package to prepend
113          * @param parameters
114          *            The parameters for the constructor call
115          * @return The created object
116          * @throws LoaderException
117          *             if the object can not be created
118          */
119         @SuppressWarnings("unchecked")
120         private static <T> T createObject(String className, String packageName, List<String> parameters) throws LoaderException {
121
122                 /* try to load class without package name. */
123                 Class<?> objectClass = null;
124                 try {
125                         objectClass = Class.forName(className);
126                 } catch (ClassNotFoundException cnfe1) {
127                         /* ignore, we’ll try again. */
128                 }
129
130                 if (objectClass == null) {
131                         try {
132                                 objectClass = Class.forName(packageName + "." + className);
133                         } catch (ClassNotFoundException cnfe1) {
134                                 /* okay, now we need to throw. */
135                                 throw new LoaderException(String.format("Could find neither class “%s” nor class “%s.”", className, packageName + "." + className), cnfe1);
136                         }
137                 }
138
139                 /* locate an eligible constructor. */
140                 Constructor<?> wantedConstructor = null;
141                 for (Constructor<?> constructor : objectClass.getConstructors()) {
142                         Class<?>[] parameterTypes = constructor.getParameterTypes();
143                         if (parameterTypes.length != parameters.size()) {
144                                 continue;
145                         }
146                         boolean compatibleTypes = true;
147                         for (Class<?> parameterType : parameterTypes) {
148                                 if (parameterType != String.class) {
149                                         compatibleTypes = false;
150                                         break;
151                                 }
152                         }
153                         if (!compatibleTypes) {
154                                 continue;
155                         }
156                         wantedConstructor = constructor;
157                 }
158
159                 if (wantedConstructor == null) {
160                         throw new LoaderException("Could not find eligible constructor.");
161                 }
162
163                 try {
164                         return (T) wantedConstructor.newInstance(parameters.toArray());
165                 } catch (IllegalArgumentException iae1) {
166                         throw new LoaderException("Could not invoke constructor.", iae1);
167                 } catch (InstantiationException ie1) {
168                         throw new LoaderException("Could not invoke constructor.", ie1);
169                 } catch (IllegalAccessException iae1) {
170                         throw new LoaderException("Could not invoke constructor.", iae1);
171                 } catch (InvocationTargetException ite1) {
172                         throw new LoaderException("Could not invoke constructor.", ite1);
173                 }
174
175         }
176
177 }