2 * Copyright (C) 2015 Roland Haeder
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.
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.
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/>.
17 package org.mxchange.jcore;
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;
47 * @author Roland Haeder
49 public class BaseFrameworkSystem implements FrameworkInterface {
53 private static ResourceBundle bundle;
56 * Instance for own properties
58 private static final Properties properties = new Properties(System.getProperties());
63 private static FrameworkInterface selfInstance;
68 private final Logger LOG;
71 * Application instance
73 private Application application;
79 private Client client;
84 private Contact contact;
89 private Manageable manager;
92 * Name of used database table, handled over to backend
94 private String tableName;
97 * DatabaseFrontend instance
99 private DatabaseFrontend wrapper;
106 LOG = LogManager.getLogger(this);
110 * Getter for this application
112 * @return Instance from this application
114 public static final FrameworkInterface getInstance () {
120 * No instances can be created of this class
122 protected BaseFrameworkSystem () {
123 // Init properties file
124 this.initProperties();
127 this.setSelfInstance();
131 * Application instance
133 * @return the application
136 public final Application getApplication () {
137 return this.application;
146 public final Logger getLogger () {
153 * @return the contactManager
156 public final Manageable getManager () {
161 * Getter for human-readable string from given key
163 * @param key Key to return
164 * @return Human-readable message
167 public final String getMessageStringFromKey (final String key) {
169 return this.getBundle().getString(key);
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.
176 * @param columnName Column name
177 * @return Value from field
178 * @throws IllegalArgumentException Some implementations may throw this.
181 public Object getValueFromColumn (final String columnName) throws IllegalArgumentException {
182 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0}", columnName)); //NOI18N
186 * Some "getter" for target class instance from given name.
188 * @param instance Instance to iterate on
189 * @param targetClass Class name to look for
190 * @return Class instance
192 @SuppressWarnings ("unchecked")
193 private Class<? extends FrameworkInterface> getClassFromTarget (final FrameworkInterface instance, final String targetClass) {
195 this.getLogger().debug(MessageFormat.format("instance={0},targetClass={1}", instance, targetClass)); //NOI18N
197 // Instance reflaction of this class
198 Class<? extends FrameworkInterface> c = instance.getClass();
201 while (!targetClass.equals(c.getSimpleName())) {
203 this.getLogger().debug(MessageFormat.format("c={0}", c.getSimpleName())); //NOI18N
205 // Get super class (causes unchecked warning)
206 c = (Class<? extends FrameworkInterface>) c.getSuperclass();
210 this.getLogger().trace(MessageFormat.format("c={0} - EXIT!", c)); //NOI18N
217 * Some "getter" for a Method instance from given method name
219 * @param instance Actual instance to call
220 * @param targetClass Target class name
221 * @param methodName Method name
222 * @return A Method instance
224 private Method getMethodFromName (final FrameworkInterface instance, final String targetClass, final String methodName) {
226 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
228 // Get target class instance
229 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
231 // Init field instance
232 Method method = null;
234 // Use reflection to get all attributes
236 method = c.getDeclaredMethod(methodName, new Class<?>[0]);
237 } catch (final SecurityException ex) {
239 this.abortProgramWithException(ex);
240 } catch (final NoSuchMethodException ex) {
242 this.abortProgramWithException(ex);
246 assert (method instanceof Method) : "method is not a Method instance"; //NOI18N
249 this.getLogger().trace(MessageFormat.format("method={0} - EXIT!", method)); //NOI18N
256 * Setter for self instance
258 private void setSelfInstance () {
259 // Need to set it here
264 * Aborts program with given exception
266 * @param throwable Any type of Throwable
268 protected final void abortProgramWithException (final Throwable throwable) {
270 this.getLogger().catching(throwable);
278 * Application instance
280 * @param application the application to set
282 protected final void setApplication (final Application application) {
283 this.application = application;
292 public final Client getClient () {
297 * Getter for bundle instance
299 * @return Resource bundle
301 protected final ResourceBundle getBundle () {
302 return BaseFrameworkSystem.bundle;
308 * @param client the client to set
310 protected final void setClient (final Client client) {
311 this.client = client;
315 * Checks if given boolean field is available and set to same value
317 * @param columnName Column name to check
318 * @param bool Boolean value
319 * @return Whether all conditions are met
322 public boolean isValueEqual (final String columnName, final boolean bool) {
324 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0},bool={1}", columnName, bool)); //NOI18N
330 * @param exception Exception to log
333 public final void logException (final Throwable exception) {
334 // Log this exception
335 this.getLogger().catching(exception);
339 * Prepares all properties, the file is written if it is not found
341 private void initProperties () {
343 this.getLogger().trace("CALLED!"); //NOI18N
346 this.getLogger().debug(MessageFormat.format("{0} properties are loaded already.", BaseFrameworkSystem.properties.size())); //NOI18N
348 // Are some properties loaded?
349 if (!BaseFrameworkSystem.properties.isEmpty()) {
350 // Some are already loaded, abort here
356 BaseFrameworkSystem.properties.load(new BufferedReader(new InputStreamReader(new FileInputStream(FrameworkInterface.PROPERTIES_CONFIG_FILE))));
359 this.getLogger().debug(MessageFormat.format("{0} properties has been loaded.", BaseFrameworkSystem.properties.size())); //NOI18N
360 } catch (final FileNotFoundException ex) {
362 this.getLogger().debug(MessageFormat.format("Properties file {0} not found: {1}", FrameworkInterface.PROPERTIES_CONFIG_FILE, ex)); //NOI18N
365 * The file is not found which is normal for first run, so
366 * initialize default values.
368 this.initPropertiesWithDefault();
371 this.writePropertiesFile();
372 } catch (final IOException ex) {
373 // Something else didn't work
374 this.abortProgramWithException(ex);
378 this.getLogger().trace("EXIT!"); //NOI18N
382 * Initializes properties with default values
384 private void initPropertiesWithDefault () {
386 this.getLogger().trace("CALLED!"); //NOI18N
388 // Init default values:
389 // Default database backend
390 BaseFrameworkSystem.properties.put("org.mxchange.database.backendType", "base64csv"); //NOI18N
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
399 this.getLogger().trace("EXIT!"); //NOI18N
403 * Writes the properties file to disk
405 private void writePropertiesFile () {
407 this.getLogger().trace("CALLED!"); //NOI18N
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);
417 this.getLogger().trace("EXIT!"); //NOI18N
421 * Converts a column name like "foo_bar" to an attribute name like "fooBar"
423 * @param columnName Column name to convert
424 * @return Attribute name
426 protected String convertColumnNameToAttribute (final String columnName) {
428 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
430 // First all lower case
431 String lower = columnName.toLowerCase();
434 StringTokenizer tokenizer = new StringTokenizer(lower, "_"); //NOI18N
437 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
443 while (tokenizer.hasMoreTokens()) {
445 String token = tokenizer.nextToken();
447 // Is later than first element?
449 // Make first character upper-case
450 char c = token.charAt(0);
451 token = String.valueOf(c).toUpperCase() + token.substring(1);
455 builder.append(token);
462 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
465 return builder.toString();
469 * Converts a column name like "foo_bar" to a method name like "getFooBar"
470 * for non-booleans and to "isFooBar" for boolean fields.
472 * @param columnName Column name to convert
473 * @param isBool Whether the parameter is boolean
474 * @return Attribute name
476 protected String convertColumnNameToGetterMethod (final String columnName, boolean isBool) {
478 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
481 StringTokenizer tokenizer = new StringTokenizer(columnName, "_"); //NOI18N
484 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
489 builder.append("is"); //NOI18N
492 builder.append("get"); //NOI18N
496 while (tokenizer.hasMoreTokens()) {
498 String token = tokenizer.nextToken();
501 this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N
503 // Make it upper-case
504 char c = token.charAt(0);
505 token = String.valueOf(c).toUpperCase() + token.substring(1);
508 builder.append(token);
512 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
515 return builder.toString();
519 * Some "getter" for an array from given string and tokenizer
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
527 protected String[] getArrayFromString (final String str, final String delimiter, final int size) {
529 this.getLogger().trace(MessageFormat.format("str={0},delimiter={1},size={2} - CALLED!", str, delimiter, size)); //NOI18N
532 StringTokenizer tokenizer = new StringTokenizer(str, delimiter);
534 // Init array and index
535 String[] tokens = new String[size];
538 // Run through all tokens
539 while (tokenizer.hasMoreTokens()) {
540 // Get current token and add it
541 tokens[index] = tokenizer.nextToken();
544 this.getLogger().debug(MessageFormat.format("Token at index{0}: {1}", index, tokens[1])); //NOI18N
551 this.getLogger().trace(MessageFormat.format("tokens({0})={1} - EXIT!", tokens.length, Arrays.toString(tokens))); //NOI18N
558 * Returns boolean field value from given method name by invoking it
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
565 protected boolean getBooleanField (final FrameworkInterface instance, final String targetClass, final String methodName) {
567 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
569 // Get method instance
570 Method method = this.getMethodFromName(instance, targetClass, methodName);
572 // Get value from field
573 Boolean value = false;
576 value = (Boolean) method.invoke(instance);
577 } catch (final IllegalArgumentException ex) {
579 this.abortProgramWithException(ex);
580 } catch (final IllegalAccessException ex) {
582 this.abortProgramWithException(ex);
583 } catch (final InvocationTargetException ex) {
585 this.abortProgramWithException(ex);
595 * @param manager the manager instance to set
597 protected final void setContactManager (final Manageable manager) {
598 this.manager = manager;
602 * Returns any field value from given method name by invoking it
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
609 protected Object getField (final FrameworkInterface instance, final String targetClass, final String methodName) {
611 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
613 // Get method to call
614 Method method = this.getMethodFromName(instance, targetClass, methodName);
616 // Get value from field
617 Object object = null;
620 object = method.invoke(instance);
621 } catch (final IllegalArgumentException ex) {
623 this.abortProgramWithException(ex);
624 } catch (final IllegalAccessException ex) {
626 this.abortProgramWithException(ex);
627 } catch (final InvocationTargetException ex) {
629 this.abortProgramWithException(ex);
637 * Getter for property which must exist
639 * @param key Key to get
640 * @return Propety value
642 protected final String getProperty (final String key) {
643 return BaseFrameworkSystem.properties.getProperty(String.format("org.mxchange.%s", key)); //NOI18N
647 * Name of used database table, handled over to backend
649 * @return the tableName
651 protected final String getTableName () {
652 return this.tableName;
656 * Name of used database table, handled over to backend
658 * @param tableName the tableName to set
660 protected final void setTableName (final String tableName) {
661 this.tableName = tableName;
665 * Getter for DatabaseFrontend instance
667 * @return DatabaseFrontend instance
669 protected final DatabaseFrontend getWrapper () {
674 * Setter for wrapper instance
676 * @param wrapper A DatabaseFrontend instance
678 protected final void setWrapper (final DatabaseFrontend wrapper) {
679 this.wrapper = wrapper;
683 * Getter for Contact instance
685 * @return Contact instance
687 protected final Contact getContact () {
692 * Setter for Contact instance
694 * @param contact A Contact instance
696 protected final void setContact (final Contact contact) {
697 this.contact = contact;
701 * Initializes i18n bundles
703 protected void initBundle () {
704 // Is the bundle set?
705 if (bundle instanceof ResourceBundle) {
707 throw new IllegalStateException("called twice");
711 bundle = ResourceBundle.getBundle(FrameworkInterface.I18N_BUNDLE_FILE); // NOI18N
715 * Checks whether the given field is a boolean field by probing it.
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
722 protected boolean isBooleanField (final FrameworkInterface instance, final String targetClass, final String columnName) {
724 this.getLogger().trace(MessageFormat.format("instance={0},targetCLass={1},columnName={2} - CALLED!", instance, targetClass, columnName)); //NOI18N
726 // Convert column name to getter name (boolean)
727 String methodName = this.convertColumnNameToGetterMethod(columnName, true);
729 // Get class instance
730 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
732 // Defauzlt is boolean
733 boolean isBool = true;
736 // Now try to instance the method
737 Method method = c.getDeclaredMethod(methodName, new Class<?>[0]);
738 } catch (final NoSuchMethodException ex) {
740 this.getLogger().debug(MessageFormat.format("Method {0} does not exist, field {1} cannot be boolean: {2}", methodName, columnName, ex)); //NOI18N
744 } catch (final SecurityException ex) {
746 this.abortProgramWithException(ex);
750 this.getLogger().trace(MessageFormat.format("isBool={0} - EXIT!", isBool)); //NOI18N
757 * Creates an iterator from given instance and class name.
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
763 protected Iterator<Object> fieldIterator (final FrameworkInterface instance, final String className) {
765 this.getLogger().trace(MessageFormat.format("instance={0},className={1} - CALLED!", instance, className));
767 // Get all attributes from given instance
768 Field[] fields = this.getClassFromTarget(instance, className).getDeclaredFields();
771 this.getLogger().debug(MessageFormat.format("Found {0} fields.", fields.length));
774 List<Object> list = new ArrayList<>(fields.length);
777 for (final Field field : fields) {
779 this.getLogger().debug(MessageFormat.format("field={0}", field.getName()));
781 // Does the field start with "$"?
782 if (field.getName().startsWith("$")) {
788 Object value = this.getValueFromColumn(field.getName());
791 this.getLogger().debug(MessageFormat.format("value={0}", value));
794 boolean added = list.add(value);
797 this.getLogger().debug("added=" + added);
801 this.getLogger().debug(MessageFormat.format("Returning iterator for {0} entries ...", list.size()));
803 // Return list iterator
804 return list.iterator();