source: trunk/autoquest-core-events/src/main/java/de/ugoe/cs/autoquest/eventcore/guimodel/GUIElementFactory.java @ 2252

Last change on this file since 2252 was 2146, checked in by pharms, 7 years ago
  • refactored GUI model so that hierarchical event target structures can also be used and created by plugins not being strictly for GUIs
File size: 13.7 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.eventcore.guimodel;
16
17import java.io.File;
18import java.io.FileInputStream;
19import java.io.FileNotFoundException;
20import java.io.IOException;
21import java.io.InputStream;
22import java.lang.reflect.Constructor;
23import java.lang.reflect.InvocationTargetException;
24import java.util.Properties;
25import java.util.logging.Level;
26
27import de.ugoe.cs.autoquest.eventcore.EventTargetModelConfigurationException;
28import de.ugoe.cs.autoquest.eventcore.HierarchicalEventTargetModel;
29import de.ugoe.cs.autoquest.eventcore.IEventTargetFactory;
30import de.ugoe.cs.autoquest.eventcore.IEventTargetSpec;
31import de.ugoe.cs.autoquest.eventcore.IHierarchicalEventTarget;
32import de.ugoe.cs.util.console.Console;
33
34/**
35 * <p>
36 * Creates {@link IGUIElement}s from a given specification. Implemented as singleton.
37 * </p>
38 *
39 * @version 1.0
40 * @author Patrick Harms
41 */
42public class GUIElementFactory implements IEventTargetFactory {
43
44    /**
45     * <p>
46     * Instance of the class (singleton)
47     * </p>
48     */
49    private static GUIElementFactory instance = new GUIElementFactory();
50
51    /**
52     * <p>
53     * Constructor. Creates a new GUIElementFactory. Private to preserve singleton property.
54     * </p>
55     */
56    private GUIElementFactory() {}
57
58    /**
59     * <p>
60     * Returns the instance of this class.
61     * </p>
62     *
63     * @return the instance
64     */
65    public static synchronized GUIElementFactory getInstance() {
66        return instance;
67    }
68
69    /**
70     * <p>
71     * A property mapping that defines which Java class is created given the type of the GUI
72     * element found in the specification.
73     * </p>
74     */
75    private Properties mappingsFromConfiguration;
76
77   
78    /* (non-Javadoc)
79     * @see IEventTargetFactory#instantiateEventTarget(IEventTargetSpec, IHierarchicalEventTarget)
80     */
81    @SuppressWarnings("unchecked")
82    @Override
83    public <T extends IHierarchicalEventTarget> T instantiateEventTarget(IEventTargetSpec specification,
84                                                                         T                parent)
85        throws EventTargetModelConfigurationException
86    {
87        if (!(specification instanceof IGUIElementSpec)) {
88            throw new IllegalArgumentException("can only handle IGUIElementSpecs as specification");
89        }
90       
91        if ((parent != null) && !(parent instanceof IGUIElement)) {
92            throw new IllegalArgumentException("can only handle IGUIElements as parent");
93        }
94       
95       return (T) instantiateEventTarget((IGUIElementSpec) specification, (IGUIElement) parent);
96    }
97       
98   
99    /**
100     * concrete implementation of {@link #instantiateEventTarget(IEventTargetSpec, IHierarchicalEventTarget)}
101     */
102    public IGUIElement instantiateEventTarget(IGUIElementSpec specification,
103                                              IGUIElement     parent)
104        throws EventTargetModelConfigurationException
105    {
106        Properties mappings = getMappingsFromConfiguration();
107        IGUIElement guiElement = null;
108        String[] typeHierarchy = specification.getTypeHierarchy();
109        int i = 0;
110        String className = null;
111       
112        while ((className == null) && (i < typeHierarchy.length)) {
113            className = mappings.getProperty(typeHierarchy[i]);
114            i++;
115        }
116       
117        if (className != null) {
118            try {
119                Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
120
121                if (!IGUIElement.class.isAssignableFrom(clazz)) {
122                    Console.traceln(Level.WARNING, "configured GUI element representing class " +
123                        className + " is no valid GUIElement derivate.");
124
125                    return null;
126                }
127
128                Constructor<?> constructor = null;
129                Class<?> parentClass = (parent == null) ? null : parent.getClass();
130
131                // search for a constructor, that perfectly matches the types
132                for (Constructor<?> candidate : clazz.getConstructors()) {
133                    if ((parentClass != null) && (candidate.getParameterTypes().length == 2) &&
134                        (candidate.getParameterTypes()[0].equals(specification.getClass())) &&
135                        (candidate.getParameterTypes()[1].equals(parentClass)))
136                    {
137                        constructor = candidate;
138                        break;
139                    }
140                    else if (parentClass == null) {
141                        if ((candidate.getParameterTypes().length >= 1) &&
142                            (candidate.getParameterTypes()[0].equals(specification.getClass())))
143                        {
144                            constructor = candidate;
145                            break;
146                        }
147                    }
148                }
149
150                if (constructor == null) {
151                    // search for an assignable constructor
152                    for (Constructor<?> candidate : clazz.getConstructors()) {
153                        if ((candidate.getParameterTypes().length == 2) &&
154                            (candidate.getParameterTypes()[0].isInstance(specification)) &&
155                            (candidate.getParameterTypes()[1].isInstance(parent)))
156                        {
157                            constructor = candidate;
158                            break;
159                        }
160                    }
161
162                }
163               
164                if (constructor != null) {
165                    guiElement = (IGUIElement) constructor.newInstance(specification, parent);
166                }
167                else {
168                    throw new NoSuchMethodException
169                        ("no constructor with two parameters and assignable parameter types for " +
170                         specification.getClass() + " and " +
171                         (parent != null ? parent.getClass() : "null") + " found in class " +
172                         clazz);
173                }
174
175            }
176            catch (ClassNotFoundException e) {
177                Console.traceln(Level.WARNING, "configured GUI element representing class " +
178                                className + " can not be loaded.");
179                throw new EventTargetModelConfigurationException
180                    ("configured GUI element representing class " + className +
181                     " can not be loaded.", e);
182            }
183            catch (SecurityException e) {
184                Console.traceln(Level.WARNING, "configured GUI element representing class " +
185                                className + " can not be instantiated due to security reasons.");
186                throw new EventTargetModelConfigurationException
187                    ("configured GUI element representing class " + className +
188                     " can not be instantiated due to security reasons.", e);
189            }
190            catch (NoSuchMethodException e) {
191                Console.traceln(Level.WARNING, "configured GUI element representing class " +
192                                className + " does not provide an appropriate constructor.");
193                throw new EventTargetModelConfigurationException
194                    ("configured GUI element representing class " + className +
195                     " does not provide an appropriate constructor.", e);
196            }
197            catch (IllegalArgumentException e) {
198                Console.traceln(Level.WARNING, "configured GUI element representing class " +
199                                className + " does not provide an appropriate constructor " +
200                                "accepting the provided parameters.");
201                throw new EventTargetModelConfigurationException
202                    ("configured GUI element representing class " + className + " does not " +
203                     "provide an appropriate constructor accepting the provided parameters.", e);
204            }
205            catch (InstantiationException e) {
206                Console.traceln(Level.WARNING, "configured GUI element representing class " +
207                                className + " can not be instantiated.");
208                throw new EventTargetModelConfigurationException
209                    ("configured GUI element representing class " + className +
210                     " can not be instantiated.", e);
211            }
212            catch (IllegalAccessException e) {
213                Console.traceln(Level.WARNING, "configured GUI element representing class " +
214                                className + " can not be instantiated.");
215                throw new EventTargetModelConfigurationException
216                    ("configured GUI element representing class " + className +
217                     " can not be instantiated.", e);
218            }
219            catch (InvocationTargetException e) {
220                Console.traceln(Level.WARNING, "configured GUI element representing class " +
221                                className + " can not be instantiated.");
222                throw new EventTargetModelConfigurationException
223                    ("configured GUI element representing class " + className +
224                     " can not be instantiated.", e);
225            }
226        }
227       
228        if (guiElement == null ) {
229            Console.traceln(Level.WARNING, "no class representing GUI elements of type " +
230                            specification.getType() + " found. Please extend GUI element " +
231                            "mapping files.");
232            throw new EventTargetModelConfigurationException
233                ("no class representing GUI elements of type " + specification.getType() +
234                 " found. Please extend GUI element mapping files");
235        }
236
237        return guiElement;
238    }
239
240    /* (non-Javadoc)
241     * @see IEventTargetFactory#instantiateGroup(String, IHierarchicalEventTarget, HierarchicalEventTargetModel)
242     */
243    @SuppressWarnings("unchecked")
244    @Override
245    public <T extends IHierarchicalEventTarget> T instantiateGroup(String                          groupName,
246                                                                   T                               parent,
247                                                                   HierarchicalEventTargetModel<T> hierarchicalEventTargetModel)
248    {
249        if (!(hierarchicalEventTargetModel instanceof GUIModel)) {
250            throw new IllegalArgumentException("can only handle GUI elements as event targets");
251        }
252       
253        if (!(parent instanceof IGUIElement)) {
254            throw new IllegalArgumentException("can only handle GUI elements as event targets");
255        }
256       
257        return (T) new GUIElementGroup
258              (groupName, (IGUIElement) parent, (GUIModel) hierarchicalEventTargetModel);
259    }
260
261    /**
262     * <p>
263     * Loads the mappings for GUI elements. All files that start with &quot;guimapping&quot;, end
264     * with &quot;.txt&quot;, and are located in the folder &quot;data/guimappings&quot; (relative
265     * to the working directory) are loaded.
266     * </p>
267     *
268     * @return loaded GUI mappings
269     */
270    private synchronized Properties getMappingsFromConfiguration()
271        throws EventTargetModelConfigurationException
272    {
273        if (mappingsFromConfiguration != null) {
274            return mappingsFromConfiguration;
275        }
276        else {
277            mappingsFromConfiguration = new Properties();
278
279            File mappingsFolder = new File("data/guimappings");
280            File[] children = mappingsFolder.listFiles();
281
282            if (children != null) {
283                for (File mappingsFile : children) {
284                    if (!mappingsFile.isDirectory() &&
285                        mappingsFile.getName().startsWith("guimapping") &&
286                        mappingsFile.getName().endsWith(".txt"))
287                    {
288                        InputStream inStream = null;
289                        try {
290                            inStream = new FileInputStream(mappingsFile);
291                            mappingsFromConfiguration.load(inStream);
292                        }
293                        catch (FileNotFoundException e) {
294                            throw new EventTargetModelConfigurationException
295                                ("could not read mapping configuration file " + mappingsFile, e);
296                        }
297                        catch (IOException e) {
298                            throw new EventTargetModelConfigurationException
299                                ("could not read mapping configuration file " + mappingsFile, e);
300                        }
301                        finally {
302                            if (inStream != null) {
303                                try {
304                                    inStream.close();
305                                }
306                                catch (IOException e) {
307                                    // ignore
308                                }
309                            }
310                        }
311                    }
312                }
313            }
314            else {
315                throw new EventTargetModelConfigurationException
316                    ("no GUI mappings file provided in folder " + mappingsFolder);
317            }
318
319            return mappingsFromConfiguration;
320        }
321    }
322}
Note: See TracBrowser for help on using the repository browser.