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.InvocationTargetException;
26 import java.lang.reflect.Method;
27 import java.text.MessageFormat;
28 import java.util.Arrays;
29 import java.util.Properties;
30 import java.util.ResourceBundle;
31 import java.util.StringTokenizer;
32 import org.apache.logging.log4j.LogManager;
33 import org.apache.logging.log4j.Logger;
34 import org.mxchange.jcore.application.Application;
35 import org.mxchange.jcore.client.Client;
36 import org.mxchange.jcore.contact.Contact;
37 import org.mxchange.jcore.database.frontend.DatabaseFrontend;
38 import org.mxchange.jcore.manager.Manageable;
43 * @author Roland Haeder
45 public class BaseFrameworkSystem implements FrameworkInterface {
49 private static ResourceBundle bundle;
52 * Instance for own properties
54 private static final Properties properties = new Properties(System.getProperties());
59 private static FrameworkInterface selfInstance;
64 private final Logger LOG;
67 * Application instance
69 private Application application;
75 private Client client;
80 private Contact contact;
85 private Manageable manager;
88 * Name of used database table, handled over to backend
90 private String tableName;
93 * DatabaseFrontend instance
95 private DatabaseFrontend wrapper;
102 LOG = LogManager.getLogger(this);
106 * Getter for this application
108 * @return Instance from this application
110 public static final FrameworkInterface getInstance () {
116 * No instances can be created of this class
118 protected BaseFrameworkSystem () {
119 // Init properties file
120 this.initProperties();
123 this.setSelfInstance();
127 * Application instance
129 * @return the application
132 public final Application getApplication () {
133 return this.application;
142 public final Logger getLogger () {
149 * @return the contactManager
152 public final Manageable getManager () {
157 * Getter for human-readable string from given key
159 * @param key Key to return
160 * @return Human-readable message
163 public final String getMessageStringFromKey (final String key) {
165 return this.getBundle().getString(key);
169 * Some "getter for a value from given column name. This name will be
170 * translated into a method name and then this method is called.
172 * @param columnName Column name
173 * @return Value from field
176 public Object getValueFromColumn (final String columnName) {
177 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0}", columnName)); //NOI18N
181 * Some "getter" for target class instance from given name.
183 * @param instance Instance to iterate on
184 * @param targetClass Class name to look for
185 * @return Class instance
187 @SuppressWarnings ("unchecked")
188 private Class<? extends FrameworkInterface> getClassFromTarget (final FrameworkInterface instance, final String targetClass) {
190 this.getLogger().debug(MessageFormat.format("instance={0},targetClass={1}", instance, targetClass)); //NOI18N
192 // Instance reflaction of this class
193 Class<? extends FrameworkInterface> c = instance.getClass();
196 while (!targetClass.equals(c.getSimpleName())) {
198 this.getLogger().debug(MessageFormat.format("c={0}", c.getSimpleName())); //NOI18N
200 // Get super class (causes unchecked warning)
201 c = (Class<? extends FrameworkInterface>) c.getSuperclass();
205 this.getLogger().trace(MessageFormat.format("c={0} - EXIT!", c)); //NOI18N
212 * Some "getter" for a Method instance from given method name
214 * @param instance Actual instance to call
215 * @param targetClass Target class name
216 * @param methodName Method name
217 * @return A Method instance
219 private Method getMethodFromName (final FrameworkInterface instance, final String targetClass, final String methodName) {
221 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
223 // Get target class instance
224 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
226 // Init field instance
227 Method method = null;
229 // Use reflection to get all attributes
231 method = c.getDeclaredMethod(methodName, new Class<?>[0]);
232 } catch (final SecurityException ex) {
234 this.abortProgramWithException(ex);
235 } catch (final NoSuchMethodException ex) {
237 this.abortProgramWithException(ex);
241 assert (method instanceof Method) : "method is not a Method instance"; //NOI18N
244 this.getLogger().trace(MessageFormat.format("method={0} - EXIT!", method)); //NOI18N
251 * Setter for self instance
253 private void setSelfInstance () {
254 // Need to set it here
259 * Aborts program with given exception
261 * @param throwable Any type of Throwable
263 protected final void abortProgramWithException (final Throwable throwable) {
265 this.getLogger().catching(throwable);
273 * Application instance
275 * @param application the application to set
277 protected final void setApplication (final Application application) {
278 this.application = application;
287 public final Client getClient () {
292 * Getter for bundle instance
294 * @return Resource bundle
296 protected final ResourceBundle getBundle () {
297 return BaseFrameworkSystem.bundle;
303 * @param client the client to set
305 protected final void setClient (final Client client) {
306 this.client = client;
310 * Checks if given boolean field is available and set to same value
312 * @param columnName Column name to check
313 * @param bool Boolean value
314 * @return Whether all conditions are met
317 public boolean isValueEqual (final String columnName, final boolean bool) {
319 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0},bool={1}", columnName, bool)); //NOI18N
325 * @param exception Exception to log
328 public final void logException (final Throwable exception) {
329 // Log this exception
330 this.getLogger().catching(exception);
334 * Prepares all properties, the file is written if it is not found
336 private void initProperties () {
338 this.getLogger().trace("CALLED!"); //NOI18N
341 this.getLogger().debug(MessageFormat.format("{0} properties are loaded already.", BaseFrameworkSystem.properties.size())); //NOI18N
343 // Are some properties loaded?
344 if (!BaseFrameworkSystem.properties.isEmpty()) {
345 // Some are already loaded, abort here
351 BaseFrameworkSystem.properties.load(new BufferedReader(new InputStreamReader(new FileInputStream(FrameworkInterface.PROPERTIES_CONFIG_FILE))));
354 this.getLogger().debug(MessageFormat.format("{0} properties has been loaded.", BaseFrameworkSystem.properties.size())); //NOI18N
355 } catch (final FileNotFoundException ex) {
357 this.getLogger().debug(MessageFormat.format("Properties file {0} not found: {1}", FrameworkInterface.PROPERTIES_CONFIG_FILE, ex)); //NOI18N
360 * The file is not found which is normal for first run, so
361 * initialize default values.
363 this.initPropertiesWithDefault();
366 this.writePropertiesFile();
367 } catch (final IOException ex) {
368 // Something else didn't work
369 this.abortProgramWithException(ex);
373 this.getLogger().trace("EXIT!"); //NOI18N
377 * Initializes properties with default values
379 private void initPropertiesWithDefault () {
381 this.getLogger().trace("CALLED!"); //NOI18N
383 // Init default values:
384 // Default database backend
385 BaseFrameworkSystem.properties.put("org.mxchange.database.backendType", "base64csv"); //NOI18N
388 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.host", "localhost"); //NOI18N
389 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.dbname", "test"); //NOI18N
390 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.login", ""); //NOI18N
391 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.password", ""); //NOI18N
394 this.getLogger().trace("EXIT!"); //NOI18N
398 * Writes the properties file to disk
400 private void writePropertiesFile () {
402 this.getLogger().trace("CALLED!"); //NOI18N
406 BaseFrameworkSystem.properties.store(new PrintWriter(FrameworkInterface.PROPERTIES_CONFIG_FILE), "This file is automatically generated. You may wish to alter it."); //NOI18N
407 } catch (final IOException ex) {
408 this.abortProgramWithException(ex);
412 this.getLogger().trace("EXIT!"); //NOI18N
416 * Converts a column name like "foo_bar" to an attribute name like "fooBar"
418 * @param columnName Column name to convert
419 * @return Attribute name
421 protected String convertColumnNameToAttribute (final String columnName) {
423 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
425 // First all lower case
426 String lower = columnName.toLowerCase();
429 StringTokenizer tokenizer = new StringTokenizer(lower, "_"); //NOI18N
432 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
438 while (tokenizer.hasMoreTokens()) {
440 String token = tokenizer.nextToken();
442 // Is later than first element?
444 // Make first character upper-case
445 char c = token.charAt(0);
446 token = String.valueOf(c).toUpperCase() + token.substring(1);
450 builder.append(token);
457 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
460 return builder.toString();
464 * Converts a column name like "foo_bar" to a method name like "getFooBar"
465 * for non-booleans and to "isFooBar" for boolean fields.
467 * @param columnName Column name to convert
468 * @param isBool Whether the parameter is boolean
469 * @return Attribute name
471 protected String convertColumnNameToGetterMethod (final String columnName, boolean isBool) {
473 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
476 StringTokenizer tokenizer = new StringTokenizer(columnName, "_"); //NOI18N
479 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
484 builder.append("is"); //NOI18N
487 builder.append("get"); //NOI18N
491 while (tokenizer.hasMoreTokens()) {
493 String token = tokenizer.nextToken();
496 this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N
498 // Make it upper-case
499 char c = token.charAt(0);
500 token = String.valueOf(c).toUpperCase() + token.substring(1);
503 builder.append(token);
507 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
510 return builder.toString();
514 * Some "getter" for an array from given string and tokenizer
516 * @param str String to tokenize and get array from
517 * @param delimiter Delimiter
518 * @param size Size of array
519 * @return Array from tokenized string
521 protected String[] getArrayFromString (final String str, final String delimiter, final int size) {
523 this.getLogger().trace(MessageFormat.format("str={0},delimiter={1},size={2} - CALLED!", str, delimiter, size)); //NOI18N
526 StringTokenizer tokenizer = new StringTokenizer(str, delimiter);
528 // Init array and index
529 String[] tokens = new String[size];
532 // Run through all tokens
533 while (tokenizer.hasMoreTokens()) {
534 // Get current token and add it
535 tokens[index] = tokenizer.nextToken();
538 this.getLogger().debug(MessageFormat.format("Token at index{0}: {1}", index, tokens[1])); //NOI18N
545 this.getLogger().trace(MessageFormat.format("tokens({0})={1} - EXIT!", tokens.length, Arrays.toString(tokens))); //NOI18N
552 * Returns boolean field value from given method call
554 * @param instance The instance to call
555 * @param targetClass Target class to look in
556 * @param methodName Method name to look for
557 * @return Boolean value from field
559 protected boolean getBooleanField (final FrameworkInterface instance, final String targetClass, final String methodName) {
561 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
563 // Get method instance
564 Method method = this.getMethodFromName(instance, targetClass, methodName);
566 // Get value from field
567 Boolean value = false;
570 value = (Boolean) method.invoke(instance);
571 } catch (final IllegalArgumentException ex) {
573 this.abortProgramWithException(ex);
574 } catch (final IllegalAccessException ex) {
576 this.abortProgramWithException(ex);
577 } catch (final InvocationTargetException ex) {
579 this.abortProgramWithException(ex);
589 * @param manager the manager instance to set
591 protected final void setContactManager (final Manageable manager) {
592 this.manager = manager;
596 * Returns any field value from given method call
598 * @param instance The instance to call
599 * @param targetClass Target class to look in
600 * @param methodName Method name to look for
601 * @return Any value from field
603 protected Object getField (final FrameworkInterface instance, final String targetClass, final String methodName) {
605 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
607 // Get method to call
608 Method method = this.getMethodFromName(instance, targetClass, methodName);
610 // Get value from field
611 Object object = null;
614 object = method.invoke(instance);
615 } catch (final IllegalArgumentException ex) {
617 this.abortProgramWithException(ex);
618 } catch (final IllegalAccessException ex) {
620 this.abortProgramWithException(ex);
621 } catch (final InvocationTargetException ex) {
623 this.abortProgramWithException(ex);
631 * Getter for property which must exist
633 * @param key Key to get
634 * @return Propety value
636 protected final String getProperty (final String key) {
637 return BaseFrameworkSystem.properties.getProperty(String.format("org.mxchange.%s", key)); //NOI18N
641 * Name of used database table, handled over to backend
643 * @return the tableName
645 protected final String getTableName () {
646 return this.tableName;
650 * Name of used database table, handled over to backend
652 * @param tableName the tableName to set
654 protected final void setTableName (final String tableName) {
655 this.tableName = tableName;
659 * Getter for DatabaseFrontend instance
661 * @return DatabaseFrontend instance
663 protected final DatabaseFrontend getWrapper () {
668 * Setter for wrapper instance
670 * @param wrapper A DatabaseFrontend instance
672 protected final void setWrapper (final DatabaseFrontend wrapper) {
673 this.wrapper = wrapper;
677 * Getter for Contact instance
679 * @return Contact instance
681 protected final Contact getContact () {
686 * Setter for Contact instance
688 * @param contact A Contact instance
690 protected final void setContact (final Contact contact) {
691 this.contact = contact;
695 * Initializes i18n bundles
697 protected void initBundle () {
698 // Is the bundle set?
699 if (bundle instanceof ResourceBundle) {
701 throw new IllegalStateException("called twice");
705 bundle = ResourceBundle.getBundle(FrameworkInterface.I18N_BUNDLE_FILE); // NOI18N
709 * Checks whether the given field is a boolean field by probing it.
711 * @param instance Instance to call
712 * @param targetClass Target class
713 * @param columnName Column name to check
714 * @return Whether the given column name represents a boolean field
716 protected boolean isBooleanField (final FrameworkInterface instance, final String targetClass, final String columnName) {
718 this.getLogger().trace(MessageFormat.format("instance={0},targetCLass={1},columnName={2} - CALLED!", instance, targetClass, columnName)); //NOI18N
720 // Convert column name to getter name (boolean)
721 String methodName = this.convertColumnNameToGetterMethod(columnName, true);
723 // Get class instance
724 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
726 // Defauzlt is boolean
727 boolean isBool = true;
730 // Now try to instance the method
731 Method method = c.getDeclaredMethod(methodName, new Class<?>[0]);
732 } catch (final NoSuchMethodException ex) {
734 this.getLogger().debug(MessageFormat.format("Method {0} does not exist, field {1} cannot be boolean: {2}", methodName, columnName, ex)); //NOI18N
738 } catch (final SecurityException ex) {
740 this.abortProgramWithException(ex);
744 this.getLogger().trace(MessageFormat.format("isBool={0} - EXIT!", isBool)); //NOI18N