]> git.mxchange.org Git - jcore.git/blob - src/org/mxchange/jcore/BaseFrameworkSystem.java
Introduced fieldIterator() + all Contact instances are now iterable over their attributes
[jcore.git] / src / org / mxchange / jcore / BaseFrameworkSystem.java
1 /*
2  * Copyright (C) 2015 Roland Haeder
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 package org.mxchange.jcore;
18
19 import java.io.BufferedReader;
20 import java.io.FileInputStream;
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.io.PrintWriter;
25 import java.lang.reflect.Field;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.text.MessageFormat;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Properties;
34 import java.util.ResourceBundle;
35 import java.util.StringTokenizer;
36 import org.apache.logging.log4j.LogManager;
37 import org.apache.logging.log4j.Logger;
38 import org.mxchange.jcore.application.Application;
39 import org.mxchange.jcore.client.Client;
40 import org.mxchange.jcore.contact.Contact;
41 import org.mxchange.jcore.database.frontend.DatabaseFrontend;
42 import org.mxchange.jcore.manager.Manageable;
43
44 /**
45  * General class
46  *
47  * @author Roland Haeder
48  */
49 public class BaseFrameworkSystem implements FrameworkInterface {
50         /**
51          * Bundle instance
52          */
53         private static ResourceBundle bundle;
54
55         /**
56          * Instance for own properties
57          */
58         private static final Properties properties = new Properties(System.getProperties());
59
60         /**
61          * Self instance
62          */
63         private static FrameworkInterface selfInstance;
64
65         /**
66          * Class' logger
67          */
68         private final Logger LOG;
69
70         /**
71          * Application instance
72          */
73         private Application application;
74
75
76         /**
77          * Client instance
78          */
79         private Client client;
80
81         /**
82          * Contact instance
83          */
84         private Contact contact;
85
86         /**
87          * Manager instance
88          */
89         private Manageable manager;
90
91         /**
92          * Name of used database table, handled over to backend
93          */
94         private String tableName;
95
96         /**
97          * DatabaseFrontend instance
98          */
99         private DatabaseFrontend wrapper;
100
101
102         /**
103          * Initialize object
104          */
105         {
106                 LOG = LogManager.getLogger(this);
107         }
108
109         /**
110          * Getter for this application
111          *
112          * @return Instance from this application
113          */
114         public static final FrameworkInterface getInstance () {
115                 // Return it
116                 return selfInstance;
117         }
118
119         /**
120          * No instances can be created of this class
121          */
122         protected BaseFrameworkSystem () {
123                 // Init properties file
124                 this.initProperties();
125
126                 // Set own instance
127                 this.setSelfInstance();
128         }
129
130         /**
131          * Application instance
132          *
133          * @return the application
134          */
135         @Override
136         public final Application getApplication () {
137                 return this.application;
138         }
139
140         /**
141          * Getter for logger
142          *
143          * @return Logger
144          */
145         @Override
146         public final Logger getLogger () {
147                 return this.LOG;
148         }
149
150         /**
151          * Manager instance
152          *
153          * @return the contactManager
154          */
155         @Override
156         public final Manageable getManager () {
157                 return this.manager;
158         }
159
160         /**
161          * Getter for human-readable string from given key
162          *
163          * @param key Key to return
164          * @return Human-readable message
165          */
166         @Override
167         public final String getMessageStringFromKey (final String key) {
168                 // Return message
169                 return this.getBundle().getString(key);
170         }
171
172         /**
173          * Some "getter for a value from given column name. This name will be
174          * translated into a method name and then this method is called.
175          *
176          * @param columnName Column name
177          * @return Value from field
178          */
179         @Override
180         public Object getValueFromColumn (final String columnName) {
181                 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0}", columnName)); //NOI18N
182         }
183
184         /**
185          * Some "getter" for target class instance from given name.
186          *
187          * @param instance Instance to iterate on
188          * @param targetClass Class name to look for
189          * @return Class instance
190          */
191         @SuppressWarnings ("unchecked")
192         private Class<? extends FrameworkInterface> getClassFromTarget (final FrameworkInterface instance, final String targetClass) {
193                 // Trace message
194                 this.getLogger().debug(MessageFormat.format("instance={0},targetClass={1}", instance, targetClass)); //NOI18N
195
196                 // Instance reflaction of this class
197                 Class<? extends FrameworkInterface> c = instance.getClass();
198
199                 // Analyze class
200                 while (!targetClass.equals(c.getSimpleName())) {
201                         // Debug message
202                         this.getLogger().debug(MessageFormat.format("c={0}", c.getSimpleName())); //NOI18N
203
204                         // Get super class (causes unchecked warning)
205                         c = (Class<? extends FrameworkInterface>) c.getSuperclass();
206                 }
207
208                 // Trace message
209                 this.getLogger().trace(MessageFormat.format("c={0} - EXIT!", c)); //NOI18N
210
211                 // Return it
212                 return c;
213         }
214
215         /**
216          * Some "getter" for a Method instance from given method name
217          *
218          * @param instance Actual instance to call
219          * @param targetClass Target class name
220          * @param methodName Method name
221          * @return A Method instance
222          */
223         private Method getMethodFromName (final FrameworkInterface instance, final String targetClass, final String methodName) {
224                 // Trace messahe
225                 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
226
227                 // Get target class instance
228                 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
229
230                 // Init field instance
231                 Method method = null;
232
233                 // Use reflection to get all attributes
234                 try {
235                         method = c.getDeclaredMethod(methodName, new Class<?>[0]);
236                 } catch (final SecurityException ex) {
237                         // Security problem
238                         this.abortProgramWithException(ex);
239                 } catch (final NoSuchMethodException ex) {
240                         // Method not found
241                         this.abortProgramWithException(ex);
242                 }
243
244                 // Assert on field
245                 assert (method instanceof Method) : "method is not a Method instance"; //NOI18N
246
247                 // Trace message
248                 this.getLogger().trace(MessageFormat.format("method={0} - EXIT!", method)); //NOI18N
249
250                 // Return it
251                 return method;
252         }
253
254         /**
255          * Setter for self instance
256          */
257         private void setSelfInstance () {
258                 // Need to set it here
259                 selfInstance = this;
260         }
261
262         /**
263          * Aborts program with given exception
264          *
265          * @param       throwable Any type of Throwable
266          */
267         protected final void abortProgramWithException (final Throwable throwable) {
268                 // Log exception ...
269                 this.getLogger().catching(throwable);
270
271                 // .. and exit
272                 System.exit(1);
273                 
274         }
275
276         /**
277          * Application instance
278          *
279          * @param application the application to set
280          */
281         protected final void setApplication (final Application application) {
282                 this.application = application;
283         }
284
285         /**
286          * Client instance
287          *
288          * @return the client
289          */
290         @Override
291         public final Client getClient () {
292                 return this.client;
293         }
294
295         /**
296          * Getter for bundle instance
297          *
298          * @return Resource bundle
299          */
300         protected final ResourceBundle getBundle () {
301                 return BaseFrameworkSystem.bundle;
302         }
303
304         /**
305          * Client instance
306          *
307          * @param client the client to set
308          */
309         protected final void setClient (final Client client) {
310                 this.client = client;
311         }
312
313         /**
314          * Checks if given boolean field is available and set to same value
315          *
316          * @param columnName Column name to check
317          * @param bool Boolean value
318          * @return Whether all conditions are met
319          */
320         @Override
321         public boolean isValueEqual (final String columnName, final boolean bool) {
322                 // Not implemented
323                 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0},bool={1}", columnName, bool)); //NOI18N
324         }
325
326         /**
327          * Log exception
328          *
329          * @param exception Exception to log
330          */
331         @Override
332         public final void logException (final Throwable exception) {
333                 // Log this exception
334                 this.getLogger().catching(exception);
335         }
336
337         /**
338          * Prepares all properties, the file is written if it is not found
339          */
340         private void initProperties () {
341                 // Trace message
342                 this.getLogger().trace("CALLED!"); //NOI18N
343
344                 // Debug message
345                 this.getLogger().debug(MessageFormat.format("{0} properties are loaded already.", BaseFrameworkSystem.properties.size())); //NOI18N
346
347                 // Are some properties loaded?
348                 if (!BaseFrameworkSystem.properties.isEmpty()) {
349                         // Some are already loaded, abort here
350                         return;
351                 }
352
353                 try {
354                         // Try to read it
355                         BaseFrameworkSystem.properties.load(new BufferedReader(new InputStreamReader(new FileInputStream(FrameworkInterface.PROPERTIES_CONFIG_FILE))));
356
357                         // Debug message
358                         this.getLogger().debug(MessageFormat.format("{0} properties has been loaded.", BaseFrameworkSystem.properties.size())); //NOI18N
359                 } catch (final FileNotFoundException ex) {
360                         // Debug message
361                         this.getLogger().debug(MessageFormat.format("Properties file {0} not found: {1}", FrameworkInterface.PROPERTIES_CONFIG_FILE, ex)); //NOI18N
362
363                         /*
364                          * The file is not found which is normal for first run, so
365                          * initialize default values.
366                          */
367                         this.initPropertiesWithDefault();
368
369                         // Write file
370                         this.writePropertiesFile();
371                 } catch (final IOException ex) {
372                         // Something else didn't work
373                         this.abortProgramWithException(ex);
374                 }
375
376                 // Trace message
377                 this.getLogger().trace("EXIT!"); //NOI18N
378         }
379
380         /**
381          * Initializes properties with default values
382          */
383         private void initPropertiesWithDefault () {
384                 // Trace message
385                 this.getLogger().trace("CALLED!"); //NOI18N
386
387                 // Init default values:
388                 // Default database backend
389                 BaseFrameworkSystem.properties.put("org.mxchange.database.backendType", "base64csv"); //NOI18N
390
391                 // For MySQL backend
392                 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.host", "localhost"); //NOI18N
393                 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.dbname", "test"); //NOI18N
394                 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.login", ""); //NOI18N
395                 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.password", ""); //NOI18N
396
397                 // Trace message
398                 this.getLogger().trace("EXIT!"); //NOI18N
399         }
400
401         /**
402          * Writes the properties file to disk
403          */
404         private void writePropertiesFile () {
405                 // Trace message
406                 this.getLogger().trace("CALLED!"); //NOI18N
407
408                 try {
409                         // Write it
410                         BaseFrameworkSystem.properties.store(new PrintWriter(FrameworkInterface.PROPERTIES_CONFIG_FILE), "This file is automatically generated. You may wish to alter it."); //NOI18N
411                 } catch (final IOException ex) {
412                         this.abortProgramWithException(ex);
413                 }
414
415                 // Trace message
416                 this.getLogger().trace("EXIT!"); //NOI18N
417         }
418
419         /**
420          * Converts a column name like "foo_bar" to an attribute name like "fooBar"
421          *
422          * @param columnName Column name to convert
423          * @return Attribute name
424          */
425         protected String convertColumnNameToAttribute (final String columnName) {
426                 // Trace message
427                 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
428
429                 // First all lower case
430                 String lower = columnName.toLowerCase();
431
432                 // Then split on "_"
433                 StringTokenizer tokenizer = new StringTokenizer(lower, "_"); //NOI18N
434
435                 // Resulting string
436                 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
437
438                 // Init counter
439                 int count = 0;
440
441                 // Walk through all
442                 while (tokenizer.hasMoreTokens()) {
443                         // Get token
444                         String token = tokenizer.nextToken();
445
446                         // Is later than first element?
447                         if (count > 0) {
448                                 // Make first character upper-case
449                                 char c = token.charAt(0);
450                                 token = String.valueOf(c).toUpperCase() + token.substring(1);
451                         }
452
453                         // Add token
454                         builder.append(token);
455
456                         // Increment counter
457                         count++;
458                 }
459
460                 // Trace message
461                 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
462
463                 // Return result
464                 return builder.toString();
465         }
466
467         /**
468          * Converts a column name like "foo_bar" to a method name like "getFooBar"
469          * for non-booleans and to "isFooBar" for boolean fields.
470          *
471          * @param columnName Column name to convert
472          * @param isBool Whether the parameter is boolean
473          * @return Attribute name
474          */
475         protected String convertColumnNameToGetterMethod (final String columnName, boolean isBool) {
476                 // Trace message
477                 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
478
479                 // Then split on "_"
480                 StringTokenizer tokenizer = new StringTokenizer(columnName, "_"); //NOI18N
481
482                 // Resulting string
483                 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
484
485                 // Is it boolean?
486                 if (isBool) {
487                         // Append "is"
488                         builder.append("is"); //NOI18N
489                 } else {
490                         // Append "get"
491                         builder.append("get"); //NOI18N
492                 }
493
494                 // Walk through all
495                 while (tokenizer.hasMoreTokens()) {
496                         // Get token
497                         String token = tokenizer.nextToken();
498
499                         // Debug message
500                         this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N
501
502                         // Make it upper-case
503                         char c = token.charAt(0);
504                         token = String.valueOf(c).toUpperCase() + token.substring(1);
505
506                         // Add token
507                         builder.append(token);
508                 }
509
510                 // Trace message
511                 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
512
513                 // Return result
514                 return builder.toString();
515         }
516
517         /**
518          * Some "getter" for an array from given string and tokenizer
519          *
520          * @param str String to tokenize and get array from
521          * @param delimiter Delimiter
522          * @param size Size of array
523          * @return Array from tokenized string
524          * @todo Get rid of size parameter
525          */
526         protected String[] getArrayFromString (final String str, final String delimiter, final int size) {
527                 // Trace message
528                 this.getLogger().trace(MessageFormat.format("str={0},delimiter={1},size={2} - CALLED!", str, delimiter, size)); //NOI18N
529                 
530                 // Get tokenizer
531                 StringTokenizer tokenizer = new StringTokenizer(str, delimiter);
532                 
533                 // Init array and index
534                 String[] tokens = new String[size];
535                 int index = 0;
536                 
537                 // Run through all tokens
538                 while (tokenizer.hasMoreTokens()) {
539                         // Get current token and add it
540                         tokens[index] = tokenizer.nextToken();
541                         
542                         // Debug message
543                         this.getLogger().debug(MessageFormat.format("Token at index{0}: {1}", index, tokens[1])); //NOI18N
544                         
545                         // Increment index
546                         index++;
547                 }
548                 
549                 // Trace message
550                 this.getLogger().trace(MessageFormat.format("tokens({0})={1} - EXIT!", tokens.length, Arrays.toString(tokens))); //NOI18N
551
552                 // Return it
553                 return tokens;
554         }
555
556         /**
557          * Returns boolean field value from given method call
558          *
559          * @param instance The instance to call
560          * @param targetClass Target class to look in
561          * @param methodName Method name to look for
562          * @return Boolean value from field
563          */
564         protected boolean getBooleanField (final FrameworkInterface instance, final String targetClass, final String methodName) {
565                 // Trace messahe
566                 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
567
568                 // Get method instance
569                 Method method = this.getMethodFromName(instance, targetClass, methodName);
570
571                 // Get value from field
572                 Boolean value = false;
573
574                 try {
575                         value = (Boolean) method.invoke(instance);
576                 } catch (final IllegalArgumentException ex) {
577                         // Other problem
578                         this.abortProgramWithException(ex);
579                 } catch (final IllegalAccessException ex) {
580                         // Other problem
581                         this.abortProgramWithException(ex);
582                 } catch (final InvocationTargetException ex) {
583                         // Other problem
584                         this.abortProgramWithException(ex);
585                 }
586
587                 // Return value
588                 return value;
589         }
590
591         /**
592          * Manager instance
593          *
594          * @param manager the manager instance to set
595          */
596         protected final void setContactManager (final Manageable manager) {
597                 this.manager = manager;
598         }
599
600         /**
601          * Returns any field value from given method call
602          *
603          * @param instance The instance to call
604          * @param targetClass Target class to look in
605          * @param methodName Method name to look for
606          * @return Any value from field
607          */
608         protected Object getField (final FrameworkInterface instance, final String targetClass, final String methodName) {
609                 // Trace messahe
610                 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
611
612                 // Get method to call
613                 Method method = this.getMethodFromName(instance, targetClass, methodName);
614
615                 // Get value from field
616                 Object object = null;
617
618                 try {
619                         object = method.invoke(instance);
620                 } catch (final IllegalArgumentException ex) {
621                         // Other problem
622                         this.abortProgramWithException(ex);
623                 } catch (final IllegalAccessException ex) {
624                         // Other problem
625                         this.abortProgramWithException(ex);
626                 } catch (final InvocationTargetException ex) {
627                         // Other problem
628                         this.abortProgramWithException(ex);
629                 }
630
631                 // Return value
632                 return object;
633         }
634
635         /**
636          * Getter for property which must exist
637          *
638          * @param key Key to get
639          * @return Propety value
640          */
641         protected final String getProperty (final String key) {
642                 return BaseFrameworkSystem.properties.getProperty(String.format("org.mxchange.%s", key)); //NOI18N
643         }
644
645         /**
646          * Name of used database table, handled over to backend
647          *
648          * @return the tableName
649          */
650         protected final String getTableName () {
651                 return this.tableName;
652         }
653
654         /**
655          * Name of used database table, handled over to backend
656          *
657          * @param tableName the tableName to set
658          */
659         protected final void setTableName (final String tableName) {
660                 this.tableName = tableName;
661         }
662
663         /**
664          * Getter for DatabaseFrontend instance
665          *
666          * @return DatabaseFrontend instance
667          */
668         protected final DatabaseFrontend getWrapper () {
669                 return this.wrapper;
670         }
671
672         /**
673          * Setter for wrapper instance
674          *
675          * @param wrapper A DatabaseFrontend instance
676          */
677         protected final void setWrapper (final DatabaseFrontend wrapper) {
678                 this.wrapper = wrapper;
679         }
680
681         /**
682          * Getter for Contact instance
683          *
684          * @return Contact instance
685          */
686         protected final Contact getContact () {
687                 return this.contact;
688         }
689
690         /**
691          * Setter for Contact instance
692          *
693          * @param contact A Contact instance
694          */
695         protected final void setContact (final Contact contact) {
696                 this.contact = contact;
697         }
698
699         /**
700          * Initializes i18n bundles
701          */
702         protected void initBundle () {
703                 // Is the bundle set?
704                 if (bundle instanceof ResourceBundle) {
705                         // Is already set
706                         throw new IllegalStateException("called twice");
707                 }
708
709                 // Set instance
710                 bundle = ResourceBundle.getBundle(FrameworkInterface.I18N_BUNDLE_FILE); // NOI18N
711         }
712
713         /**
714          * Checks whether the given field is a boolean field by probing it.
715          * 
716          * @param instance Instance to call
717          * @param targetClass Target class
718          * @param columnName Column name to check
719          * @return Whether the given column name represents a boolean field
720          */
721         protected boolean isBooleanField (final FrameworkInterface instance, final String targetClass, final String columnName) {
722                 // Trace message
723                 this.getLogger().trace(MessageFormat.format("instance={0},targetCLass={1},columnName={2} - CALLED!", instance, targetClass, columnName)); //NOI18N
724
725                 // Convert column name to getter name (boolean)
726                 String methodName = this.convertColumnNameToGetterMethod(columnName, true);
727
728                 // Get class instance
729                 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
730
731                 // Defauzlt is boolean
732                 boolean isBool = true;
733
734                 try {
735                         // Now try to instance the method
736                         Method method = c.getDeclaredMethod(methodName, new Class<?>[0]);
737                 } catch (final NoSuchMethodException ex) {
738                         // Debug message
739                         this.getLogger().debug(MessageFormat.format("Method {0} does not exist, field {1} cannot be boolean: {2}", methodName, columnName, ex)); //NOI18N
740
741                         // Not found
742                         isBool = false;
743                 } catch (final SecurityException ex) {
744                         // Really bad?
745                         this.abortProgramWithException(ex);
746                 }
747
748                 // Trace message
749                 this.getLogger().trace(MessageFormat.format("isBool={0} - EXIT!", isBool)); //NOI18N
750
751                 // Return result
752                 return isBool;
753         }
754
755         /**
756          * Creates an iterator from given instance and class name. The class name
757          * is required in getValueFromColumn() to make a proper call.
758          * 
759          * @param instance Instance to run getter calls on
760          * @param className Class name to iterate over
761          * @return An iterator over all object's fields
762          */
763         protected Iterator<Object> fieldIterator (final FrameworkInterface instance, final String className) {
764                 // Trace message
765                 this.getLogger().trace(MessageFormat.format("instance={0},className={1} - CALLED!", instance, className));
766
767                 // Get all attributes from given instance
768                 Field[] fields = instance.getClass().getDeclaredFields();
769
770                 // A list is fine
771                 List<Object> list = new ArrayList<>(fields.length);
772
773                 // Walk through all
774                 for (final Field field : fields) {
775                         // Get value from it
776                         Object value = this.getValueFromColumn(field.getName());
777
778                         // Add it to list
779                         assert(list.add(value)) : MessageFormat.format("value {0} has not been added", value);
780                 }
781
782                 // Debug message
783                 this.getLogger().debug(MessageFormat.format("Returning iterator for {0} entries ...", list.size()));
784
785                 // Return list iterator
786                 return list.iterator();
787         }
788 }