]> git.mxchange.org Git - jcore.git/blob - src/org/mxchange/jcore/BaseFrameworkSystem.java
Need to be changed to Object
[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.Arrays;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Map;
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          * @throws  IllegalArgumentException Some implementations may throw this.
179          */
180         @Override
181         public Object getValueFromColumn (final String columnName) throws IllegalArgumentException {
182                 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0}", columnName)); //NOI18N
183         }
184
185         /**
186          * Some "getter" for target class instance from given name.
187          *
188          * @param instance Instance to iterate on
189          * @param targetClass Class name to look for
190          * @return Class instance
191          */
192         @SuppressWarnings ("unchecked")
193         private Class<? extends FrameworkInterface> getClassFromTarget (final FrameworkInterface instance, final String targetClass) {
194                 // Trace message
195                 this.getLogger().debug(MessageFormat.format("instance={0},targetClass={1}", instance, targetClass)); //NOI18N
196
197                 // Instance reflaction of this class
198                 Class<? extends FrameworkInterface> c = instance.getClass();
199
200                 // Analyze class
201                 while (!targetClass.equals(c.getSimpleName())) {
202                         // Debug message
203                         this.getLogger().debug(MessageFormat.format("c={0}", c.getSimpleName())); //NOI18N
204
205                         // Get super class (causes unchecked warning)
206                         c = (Class<? extends FrameworkInterface>) c.getSuperclass();
207                 }
208
209                 // Trace message
210                 this.getLogger().trace(MessageFormat.format("c={0} - EXIT!", c)); //NOI18N
211
212                 // Return it
213                 return c;
214         }
215
216         /**
217          * Some "getter" for a Method instance from given method name
218          *
219          * @param instance Actual instance to call
220          * @param targetClass Target class name
221          * @param methodName Method name
222          * @return A Method instance
223          */
224         private Method getMethodFromName (final FrameworkInterface instance, final String targetClass, final String methodName) {
225                 // Trace messahe
226                 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
227
228                 // Get target class instance
229                 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
230
231                 // Init field instance
232                 Method method = null;
233
234                 // Use reflection to get all attributes
235                 try {
236                         method = c.getDeclaredMethod(methodName, new Class<?>[0]);
237                 } catch (final SecurityException ex) {
238                         // Security problem
239                         this.abortProgramWithException(ex);
240                 } catch (final NoSuchMethodException ex) {
241                         // Method not found
242                         this.abortProgramWithException(ex);
243                 }
244
245                 // Assert on field
246                 assert (method instanceof Method) : "method is not a Method instance"; //NOI18N
247
248                 // Trace message
249                 this.getLogger().trace(MessageFormat.format("method={0} - EXIT!", method)); //NOI18N
250
251                 // Return it
252                 return method;
253         }
254
255         /**
256          * Setter for self instance
257          */
258         private void setSelfInstance () {
259                 // Need to set it here
260                 selfInstance = this;
261         }
262
263         /**
264          * Aborts program with given exception
265          *
266          * @param       throwable Any type of Throwable
267          */
268         protected final void abortProgramWithException (final Throwable throwable) {
269                 // Log exception ...
270                 this.getLogger().catching(throwable);
271
272                 // .. and exit
273                 System.exit(1);
274                 
275         }
276
277         /**
278          * Application instance
279          *
280          * @param application the application to set
281          */
282         protected final void setApplication (final Application application) {
283                 this.application = application;
284         }
285
286         /**
287          * Client instance
288          *
289          * @return the client
290          */
291         @Override
292         public final Client getClient () {
293                 return this.client;
294         }
295
296         /**
297          * Getter for bundle instance
298          *
299          * @return Resource bundle
300          */
301         protected final ResourceBundle getBundle () {
302                 return BaseFrameworkSystem.bundle;
303         }
304
305         /**
306          * Client instance
307          *
308          * @param client the client to set
309          */
310         protected final void setClient (final Client client) {
311                 this.client = client;
312         }
313
314         /**
315          * Checks if given boolean field is available and set to same value
316          *
317          * @param columnName Column name to check
318          * @param bool Boolean value
319          * @return Whether all conditions are met
320          */
321         @Override
322         public boolean isValueEqual (final String columnName, final boolean bool) {
323                 // Not implemented
324                 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0},bool={1}", columnName, bool)); //NOI18N
325         }
326
327         /**
328          * Log exception
329          *
330          * @param exception Exception to log
331          */
332         @Override
333         public final void logException (final Throwable exception) {
334                 // Log this exception
335                 this.getLogger().catching(exception);
336         }
337
338         /**
339          * Prepares all properties, the file is written if it is not found
340          */
341         private void initProperties () {
342                 // Trace message
343                 this.getLogger().trace("CALLED!"); //NOI18N
344
345                 // Debug message
346                 this.getLogger().debug(MessageFormat.format("{0} properties are loaded already.", BaseFrameworkSystem.properties.size())); //NOI18N
347
348                 // Are some properties loaded?
349                 if (!BaseFrameworkSystem.properties.isEmpty()) {
350                         // Some are already loaded, abort here
351                         return;
352                 }
353
354                 try {
355                         // Try to read it
356                         BaseFrameworkSystem.properties.load(new BufferedReader(new InputStreamReader(new FileInputStream(FrameworkInterface.PROPERTIES_CONFIG_FILE))));
357
358                         // Debug message
359                         this.getLogger().debug(MessageFormat.format("{0} properties has been loaded.", BaseFrameworkSystem.properties.size())); //NOI18N
360                 } catch (final FileNotFoundException ex) {
361                         // Debug message
362                         this.getLogger().debug(MessageFormat.format("Properties file {0} not found: {1}", FrameworkInterface.PROPERTIES_CONFIG_FILE, ex)); //NOI18N
363
364                         /*
365                          * The file is not found which is normal for first run, so
366                          * initialize default values.
367                          */
368                         this.initPropertiesWithDefault();
369
370                         // Write file
371                         this.writePropertiesFile();
372                 } catch (final IOException ex) {
373                         // Something else didn't work
374                         this.abortProgramWithException(ex);
375                 }
376
377                 // Trace message
378                 this.getLogger().trace("EXIT!"); //NOI18N
379         }
380
381         /**
382          * Initializes properties with default values
383          */
384         private void initPropertiesWithDefault () {
385                 // Trace message
386                 this.getLogger().trace("CALLED!"); //NOI18N
387
388                 // Init default values:
389                 // Default database backend
390                 BaseFrameworkSystem.properties.put("org.mxchange.database.backendType", "base64csv"); //NOI18N
391
392                 // For MySQL backend
393                 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.host", "localhost"); //NOI18N
394                 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.dbname", "test"); //NOI18N
395                 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.login", ""); //NOI18N
396                 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.password", ""); //NOI18N
397
398                 // Trace message
399                 this.getLogger().trace("EXIT!"); //NOI18N
400         }
401
402         /**
403          * Writes the properties file to disk
404          */
405         private void writePropertiesFile () {
406                 // Trace message
407                 this.getLogger().trace("CALLED!"); //NOI18N
408
409                 try {
410                         // Write it
411                         BaseFrameworkSystem.properties.store(new PrintWriter(FrameworkInterface.PROPERTIES_CONFIG_FILE), "This file is automatically generated. You may wish to alter it."); //NOI18N
412                 } catch (final IOException ex) {
413                         this.abortProgramWithException(ex);
414                 }
415
416                 // Trace message
417                 this.getLogger().trace("EXIT!"); //NOI18N
418         }
419
420         /**
421          * Converts a column name like "foo_bar" to an attribute name like "fooBar"
422          *
423          * @param columnName Column name to convert
424          * @return Attribute name
425          */
426         protected String convertColumnNameToAttribute (final String columnName) {
427                 // Trace message
428                 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
429
430                 // First all lower case
431                 String lower = columnName.toLowerCase();
432
433                 // Then split on "_"
434                 StringTokenizer tokenizer = new StringTokenizer(lower, "_"); //NOI18N
435
436                 // Resulting string
437                 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
438
439                 // Init counter
440                 int count = 0;
441
442                 // Walk through all
443                 while (tokenizer.hasMoreTokens()) {
444                         // Get token
445                         String token = tokenizer.nextToken();
446
447                         // Is later than first element?
448                         if (count > 0) {
449                                 // Make first character upper-case
450                                 char c = token.charAt(0);
451                                 token = String.valueOf(c).toUpperCase() + token.substring(1);
452                         }
453
454                         // Add token
455                         builder.append(token);
456
457                         // Increment counter
458                         count++;
459                 }
460
461                 // Trace message
462                 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
463
464                 // Return result
465                 return builder.toString();
466         }
467
468         /**
469          * Converts a column name like "foo_bar" to a method name like "getFooBar"
470          * for non-booleans and to "isFooBar" for boolean fields.
471          *
472          * @param columnName Column name to convert
473          * @param isBool Whether the parameter is boolean
474          * @return Attribute name
475          */
476         protected String convertColumnNameToGetterMethod (final String columnName, boolean isBool) {
477                 // Trace message
478                 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
479
480                 // Then split on "_"
481                 StringTokenizer tokenizer = new StringTokenizer(columnName, "_"); //NOI18N
482
483                 // Resulting string
484                 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
485
486                 // Is it boolean?
487                 if (isBool) {
488                         // Append "is"
489                         builder.append("is"); //NOI18N
490                 } else {
491                         // Append "get"
492                         builder.append("get"); //NOI18N
493                 }
494
495                 // Walk through all
496                 while (tokenizer.hasMoreTokens()) {
497                         // Get token
498                         String token = tokenizer.nextToken();
499
500                         // Debug message
501                         this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N
502
503                         // Make it upper-case
504                         char c = token.charAt(0);
505                         token = String.valueOf(c).toUpperCase() + token.substring(1);
506
507                         // Add token
508                         builder.append(token);
509                 }
510
511                 // Trace message
512                 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
513
514                 // Return result
515                 return builder.toString();
516         }
517
518         /**
519          * Some "getter" for an array from given string and tokenizer
520          *
521          * @param str String to tokenize and get array from
522          * @param delimiter Delimiter
523          * @param size Size of array
524          * @return Array from tokenized string
525          * @todo Get rid of size parameter
526          */
527         protected String[] getArrayFromString (final String str, final String delimiter, final int size) {
528                 // Trace message
529                 this.getLogger().trace(MessageFormat.format("str={0},delimiter={1},size={2} - CALLED!", str, delimiter, size)); //NOI18N
530                 
531                 // Get tokenizer
532                 StringTokenizer tokenizer = new StringTokenizer(str, delimiter);
533                 
534                 // Init array and index
535                 String[] tokens = new String[size];
536                 int index = 0;
537                 
538                 // Run through all tokens
539                 while (tokenizer.hasMoreTokens()) {
540                         // Get current token and add it
541                         tokens[index] = tokenizer.nextToken();
542                         
543                         // Debug message
544                         this.getLogger().debug(MessageFormat.format("Token at index{0}: {1}", index, tokens[1])); //NOI18N
545                         
546                         // Increment index
547                         index++;
548                 }
549                 
550                 // Trace message
551                 this.getLogger().trace(MessageFormat.format("tokens({0})={1} - EXIT!", tokens.length, Arrays.toString(tokens))); //NOI18N
552
553                 // Return it
554                 return tokens;
555         }
556
557         /**
558          * Returns boolean field value from given method name by invoking it
559          *
560          * @param instance The instance to call
561          * @param targetClass Target class to look in
562          * @param methodName Method name to look for
563          * @return Boolean value from field
564          */
565         protected boolean getBooleanField (final FrameworkInterface instance, final String targetClass, final String methodName) {
566                 // Trace messahe
567                 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
568
569                 // Get method instance
570                 Method method = this.getMethodFromName(instance, targetClass, methodName);
571
572                 // Get value from field
573                 Boolean value = false;
574
575                 try {
576                         value = (Boolean) method.invoke(instance);
577                 } catch (final IllegalArgumentException ex) {
578                         // Other problem
579                         this.abortProgramWithException(ex);
580                 } catch (final IllegalAccessException ex) {
581                         // Other problem
582                         this.abortProgramWithException(ex);
583                 } catch (final InvocationTargetException ex) {
584                         // Other problem
585                         this.abortProgramWithException(ex);
586                 }
587
588                 // Return value
589                 return value;
590         }
591
592         /**
593          * Manager instance
594          *
595          * @param manager the manager instance to set
596          */
597         protected final void setContactManager (final Manageable manager) {
598                 this.manager = manager;
599         }
600
601         /**
602          * Returns any field value from given method name by invoking it
603          *
604          * @param instance The instance to call
605          * @param targetClass Target class to look in
606          * @param methodName Method name to look for
607          * @return Any value from field
608          */
609         protected Object getField (final FrameworkInterface instance, final String targetClass, final String methodName) {
610                 // Trace messahe
611                 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
612
613                 // Get method to call
614                 Method method = this.getMethodFromName(instance, targetClass, methodName);
615
616                 // Get value from field
617                 Object object = null;
618
619                 try {
620                         object = method.invoke(instance);
621                 } catch (final IllegalArgumentException ex) {
622                         // Other problem
623                         this.abortProgramWithException(ex);
624                 } catch (final IllegalAccessException ex) {
625                         // Other problem
626                         this.abortProgramWithException(ex);
627                 } catch (final InvocationTargetException ex) {
628                         // Other problem
629                         this.abortProgramWithException(ex);
630                 }
631
632                 // Return value
633                 return object;
634         }
635
636         /**
637          * Getter for property which must exist
638          *
639          * @param key Key to get
640          * @return Propety value
641          */
642         protected final String getProperty (final String key) {
643                 return BaseFrameworkSystem.properties.getProperty(String.format("org.mxchange.%s", key)); //NOI18N
644         }
645
646         /**
647          * Name of used database table, handled over to backend
648          *
649          * @return the tableName
650          */
651         protected final String getTableName () {
652                 return this.tableName;
653         }
654
655         /**
656          * Name of used database table, handled over to backend
657          *
658          * @param tableName the tableName to set
659          */
660         protected final void setTableName (final String tableName) {
661                 this.tableName = tableName;
662         }
663
664         /**
665          * Getter for DatabaseFrontend instance
666          *
667          * @return DatabaseFrontend instance
668          */
669         protected final DatabaseFrontend getWrapper () {
670                 return this.wrapper;
671         }
672
673         /**
674          * Setter for wrapper instance
675          *
676          * @param wrapper A DatabaseFrontend instance
677          */
678         protected final void setWrapper (final DatabaseFrontend wrapper) {
679                 this.wrapper = wrapper;
680         }
681
682         /**
683          * Getter for Contact instance
684          *
685          * @return Contact instance
686          */
687         protected final Contact getContact () {
688                 return this.contact;
689         }
690
691         /**
692          * Setter for Contact instance
693          *
694          * @param contact A Contact instance
695          */
696         protected final void setContact (final Contact contact) {
697                 this.contact = contact;
698         }
699
700         /**
701          * Initializes i18n bundles
702          */
703         protected void initBundle () {
704                 // Is the bundle set?
705                 if (bundle instanceof ResourceBundle) {
706                         // Is already set
707                         throw new IllegalStateException("called twice");
708                 }
709
710                 // Set instance
711                 bundle = ResourceBundle.getBundle(FrameworkInterface.I18N_BUNDLE_FILE); // NOI18N
712         }
713
714         /**
715          * Checks whether the given field is a boolean field by probing it.
716          * 
717          * @param instance Instance to call
718          * @param targetClass Target class
719          * @param columnName Column name to check
720          * @return Whether the given column name represents a boolean field
721          */
722         protected boolean isBooleanField (final FrameworkInterface instance, final String targetClass, final String columnName) {
723                 // Trace message
724                 this.getLogger().trace(MessageFormat.format("instance={0},targetCLass={1},columnName={2} - CALLED!", instance, targetClass, columnName)); //NOI18N
725
726                 // Convert column name to getter name (boolean)
727                 String methodName = this.convertColumnNameToGetterMethod(columnName, true);
728
729                 // Get class instance
730                 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
731
732                 // Defauzlt is boolean
733                 boolean isBool = true;
734
735                 try {
736                         // Now try to instance the method
737                         Method method = c.getDeclaredMethod(methodName, new Class<?>[0]);
738                 } catch (final NoSuchMethodException ex) {
739                         // Debug message
740                         this.getLogger().debug(MessageFormat.format("Method {0} does not exist, field {1} cannot be boolean: {2}", methodName, columnName, ex)); //NOI18N
741
742                         // Not found
743                         isBool = false;
744                 } catch (final SecurityException ex) {
745                         // Really bad?
746                         this.abortProgramWithException(ex);
747                 }
748
749                 // Trace message
750                 this.getLogger().trace(MessageFormat.format("isBool={0} - EXIT!", isBool)); //NOI18N
751
752                 // Return result
753                 return isBool;
754         }
755
756         /**
757          * Creates an iterator from given instance and class name.
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<Map.Entry<Field, 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 = this.getClassFromTarget(instance, className).getDeclaredFields();
769
770                 // Debug message
771                 this.getLogger().debug(MessageFormat.format("Found {0} fields.", fields.length));
772
773                 // A simple map with K=fieldName and V=Value is fine
774                 Map<Field, Object> map = new HashMap<>(fields.length);
775
776                 // Walk through all
777                 for (final Field field : fields) {
778                         // Debug log
779                         this.getLogger().debug(MessageFormat.format("field={0}", field.getName()));
780
781                         // Does the field start with "$"?
782                         if (field.getName().startsWith("$")) {
783                                 // Skip it silently
784                                 continue;
785                         }
786
787                         // Get value from it
788                         Object value = this.getValueFromColumn(field.getName());
789
790                         // Debug message
791                         this.getLogger().debug(MessageFormat.format("value={0}", value));
792
793                         // Add it to list
794                         map.put(field, value);
795                 }
796
797                 // Debug message
798                 this.getLogger().debug(MessageFormat.format("Returning iterator for {0} entries ...", map.size()));
799
800                 // Return list iterator
801                 return map.entrySet().iterator();
802         }
803
804         /**
805          * Converts null to empty string or leaves original string.
806          *
807          * @param str Any string
808          * @return Empty string if null or original string
809          */
810         protected Object convertNullToEmpty (final Object str) {
811                 // Trace message
812                 this.getLogger().trace(MessageFormat.format("str={0}", str));
813
814                 // Is it null?
815                 if (str == null) {
816                         // Return empty string
817                         return "";
818                 }
819
820                 // Trace message
821                 this.getLogger().trace(MessageFormat.format("str={0} - EXIT!", str));
822
823                 // Return it
824                 return str;
825         }
826 }