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.database.frontend.DatabaseFrontend;
37 import org.mxchange.jcore.manager.Manageable;
42 * @author Roland Haeder
44 public class BaseFrameworkSystem implements FrameworkInterface {
48 private static ResourceBundle bundle;
51 * Instance for own properties
53 private static final Properties properties = new Properties(System.getProperties());
58 private static FrameworkInterface selfInstance;
63 private final Logger LOG;
66 * Application instance
68 private Application application;
74 private Client client;
79 private Manageable manager;
82 * Name of used database table, handled over to backend
84 private String tableName;
87 * DatabaseFrontend instance
89 private DatabaseFrontend wrapper;
96 LOG = LogManager.getLogger(this);
100 * Getter for this application
102 * @return Instance from this application
104 public static final FrameworkInterface getInstance () {
110 * No instances can be created of this class
112 protected BaseFrameworkSystem () {
113 // Init properties file
114 this.initProperties();
117 this.setSelfInstance();
121 * Application instance
123 * @return the application
126 public final Application getApplication () {
127 return this.application;
136 public final Logger getLogger () {
143 * @return the contactManager
146 public final Manageable getManager () {
151 * Getter for human-readable string from given key
153 * @param key Key to return
154 * @return Human-readable message
157 public final String getMessageStringFromKey (final String key) {
159 return this.getBundle().getString(key);
163 * Some "getter for a value from given column name. This name will be
164 * translated into a method name and then this method is called.
166 * @param columnName Column name
167 * @return Value from field
170 public Object getValueFromColumn (final String columnName) {
171 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0}", columnName)); //NOI18N
175 * Some "getter" for target class instance from given name.
177 * @param instance Instance to iterate on
178 * @param targetClass Class name to look for
179 * @return Class instance
181 @SuppressWarnings ("unchecked")
182 private Class<? extends FrameworkInterface> getClassFromTarget (final FrameworkInterface instance, final String targetClass) {
184 this.getLogger().debug(MessageFormat.format("instance={0},targetClass={1}", instance, targetClass)); //NOI18N
186 // Instance reflaction of this class
187 Class<? extends FrameworkInterface> c = instance.getClass();
190 while (!targetClass.equals(c.getSimpleName())) {
192 this.getLogger().debug(MessageFormat.format("c={0}", c.getSimpleName())); //NOI18N
194 // Get super class (causes unchecked warning)
195 c = (Class<? extends FrameworkInterface>) c.getSuperclass();
199 this.getLogger().trace(MessageFormat.format("c={0} - EXIT!", c)); //NOI18N
206 * Some "getter" for a Method instance from given method name
208 * @param instance Actual instance to call
209 * @param targetClass Target class name
210 * @param methodName Method name
211 * @return A Method instance
213 private Method getMethodFromName (final FrameworkInterface instance, final String targetClass, final String methodName) {
215 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
217 // Get target class instance
218 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
220 // Init field instance
221 Method method = null;
223 // Use reflection to get all attributes
225 method = c.getDeclaredMethod(methodName, new Class<?>[0]);
226 } catch (final SecurityException ex) {
228 this.abortProgramWithException(ex);
229 } catch (final NoSuchMethodException ex) {
231 this.abortProgramWithException(ex);
235 assert (method instanceof Method) : "method is not a Method instance"; //NOI18N
238 this.getLogger().trace(MessageFormat.format("method={0} - EXIT!", method)); //NOI18N
245 * Setter for self instance
247 private void setSelfInstance () {
248 // Need to set it here
253 * Aborts program with given exception
255 * @param throwable Any type of Throwable
257 protected final void abortProgramWithException (final Throwable throwable) {
259 this.getLogger().catching(throwable);
267 * Application instance
269 * @param application the application to set
271 protected final void setApplication (final Application application) {
272 this.application = application;
281 public final Client getClient () {
286 * Getter for bundle instance
288 * @return Resource bundle
290 protected final ResourceBundle getBundle () {
291 return BaseFrameworkSystem.bundle;
297 * @param client the client to set
299 protected final void setClient (final Client client) {
300 this.client = client;
304 * Checks if given boolean field is available and set to same value
306 * @param columnName Column name to check
307 * @param bool Boolean value
308 * @return Whether all conditions are met
311 public boolean isValueEqual (final String columnName, final boolean bool) {
313 throw new UnsupportedOperationException(MessageFormat.format("Not implemented. columnName={0},bool={1}", columnName, bool)); //NOI18N
319 * @param exception Exception to log
322 public final void logException (final Throwable exception) {
323 // Log this exception
324 this.getLogger().catching(exception);
328 * Prepares all properties, the file is written if it is not found
330 private void initProperties () {
332 this.getLogger().trace("CALLED!"); //NOI18N
335 this.getLogger().debug(MessageFormat.format("{0} properties are loaded already.", BaseFrameworkSystem.properties.size())); //NOI18N
337 // Are some properties loaded?
338 if (!BaseFrameworkSystem.properties.isEmpty()) {
339 // Some are already loaded, abort here
345 BaseFrameworkSystem.properties.load(new BufferedReader(new InputStreamReader(new FileInputStream(FrameworkInterface.PROPERTIES_CONFIG_FILE))));
348 this.getLogger().debug(MessageFormat.format("{0} properties has been loaded.", BaseFrameworkSystem.properties.size())); //NOI18N
349 } catch (final FileNotFoundException ex) {
351 this.getLogger().debug(MessageFormat.format("Properties file {0} not found: {1}", FrameworkInterface.PROPERTIES_CONFIG_FILE, ex)); //NOI18N
354 * The file is not found which is normal for first run, so
355 * initialize default values.
357 this.initPropertiesWithDefault();
360 this.writePropertiesFile();
361 } catch (final IOException ex) {
362 // Something else didn't work
363 this.abortProgramWithException(ex);
367 this.getLogger().trace("EXIT!"); //NOI18N
371 * Initializes properties with default values
373 private void initPropertiesWithDefault () {
375 this.getLogger().trace("CALLED!"); //NOI18N
377 // Init default values:
378 // Default database backend
379 BaseFrameworkSystem.properties.put("org.mxchange.database.backendType", "base64csv"); //NOI18N
382 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.host", "localhost"); //NOI18N
383 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.dbname", "test"); //NOI18N
384 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.login", ""); //NOI18N
385 BaseFrameworkSystem.properties.put("org.mxchange.database.mysql.password", ""); //NOI18N
388 this.getLogger().trace("EXIT!"); //NOI18N
392 * Writes the properties file to disk
394 private void writePropertiesFile () {
396 this.getLogger().trace("CALLED!"); //NOI18N
400 BaseFrameworkSystem.properties.store(new PrintWriter(FrameworkInterface.PROPERTIES_CONFIG_FILE), "This file is automatically generated. You may wish to alter it."); //NOI18N
401 } catch (final IOException ex) {
402 this.abortProgramWithException(ex);
406 this.getLogger().trace("EXIT!"); //NOI18N
410 * Converts a column name like "foo_bar" to an attribute name like "fooBar"
412 * @param columnName Column name to convert
413 * @return Attribute name
415 protected String convertColumnNameToAttribute (final String columnName) {
417 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
419 // First all lower case
420 String lower = columnName.toLowerCase();
423 StringTokenizer tokenizer = new StringTokenizer(lower, "_"); //NOI18N
426 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
432 while (tokenizer.hasMoreTokens()) {
434 String token = tokenizer.nextToken();
436 // Is later than first element?
438 // Make first character upper-case
439 char c = token.charAt(0);
440 token = String.valueOf(c).toUpperCase() + token.substring(1);
444 builder.append(token);
451 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
454 return builder.toString();
458 * Converts a column name like "foo_bar" to a method name like "getFooBar"
459 * for non-booleans and to "isFooBar" for boolean fields.
461 * @param columnName Column name to convert
462 * @param isBool Whether the parameter is boolean
463 * @return Attribute name
465 protected String convertColumnNameToGetterMethod (final String columnName, boolean isBool) {
467 this.getLogger().trace(MessageFormat.format("columnName={0} - CALLED!", columnName)); //NOI18N
470 StringTokenizer tokenizer = new StringTokenizer(columnName, "_"); //NOI18N
473 StringBuilder builder = new StringBuilder(tokenizer.countTokens());
478 builder.append("is"); //NOI18N
481 builder.append("get"); //NOI18N
485 while (tokenizer.hasMoreTokens()) {
487 String token = tokenizer.nextToken();
490 this.getLogger().debug(MessageFormat.format("token={0}", token)); //NOI18N
492 // Make it upper-case
493 char c = token.charAt(0);
494 token = String.valueOf(c).toUpperCase() + token.substring(1);
497 builder.append(token);
501 this.getLogger().trace(MessageFormat.format("builder={0} - EXIT!", builder)); //NOI18N
504 return builder.toString();
508 * Some "getter" for an array from given string and tokenizer
510 * @param str String to tokenize and get array from
511 * @param delimiter Delimiter
512 * @param size Size of array
513 * @return Array from tokenized string
515 protected String[] getArrayFromString (final String str, final String delimiter, final int size) {
517 this.getLogger().trace(MessageFormat.format("str={0},delimiter={1},size={2} - CALLED!", str, delimiter, size)); //NOI18N
520 StringTokenizer tokenizer = new StringTokenizer(str, delimiter);
522 // Init array and index
523 String[] tokens = new String[size];
526 // Run through all tokens
527 while (tokenizer.hasMoreTokens()) {
528 // Get current token and add it
529 tokens[index] = tokenizer.nextToken();
532 this.getLogger().debug(MessageFormat.format("Token at index{0}: {1}", index, tokens[1])); //NOI18N
539 this.getLogger().trace(MessageFormat.format("tokens({0})={1} - EXIT!", tokens.length, Arrays.toString(tokens))); //NOI18N
546 * Returns boolean field value from given method call
548 * @param instance The instance to call
549 * @param targetClass Target class to look in
550 * @param methodName Method name to look for
551 * @return Boolean value from field
553 protected boolean getBooleanField (final FrameworkInterface instance, final String targetClass, final String methodName) {
555 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
557 // Get method instance
558 Method method = this.getMethodFromName(instance, targetClass, methodName);
560 // Get value from field
561 Boolean value = false;
564 value = (Boolean) method.invoke(instance);
565 } catch (final IllegalArgumentException ex) {
567 this.abortProgramWithException(ex);
568 } catch (final IllegalAccessException ex) {
570 this.abortProgramWithException(ex);
571 } catch (final InvocationTargetException ex) {
573 this.abortProgramWithException(ex);
583 * @param manager the manager instance to set
585 protected final void setContactManager (final Manageable manager) {
586 this.manager = manager;
590 * Returns any field value from given method call
592 * @param instance The instance to call
593 * @param targetClass Target class to look in
594 * @param methodName Method name to look for
595 * @return Any value from field
597 protected Object getField (final FrameworkInterface instance, final String targetClass, final String methodName) {
599 this.getLogger().trace(MessageFormat.format("targetClass={0},methodName={1}", targetClass, methodName)); //NOI18N
601 // Get method to call
602 Method method = this.getMethodFromName(instance, targetClass, methodName);
604 // Get value from field
605 Object object = null;
608 object = method.invoke(instance);
609 } catch (final IllegalArgumentException ex) {
611 this.abortProgramWithException(ex);
612 } catch (final IllegalAccessException ex) {
614 this.abortProgramWithException(ex);
615 } catch (final InvocationTargetException ex) {
617 this.abortProgramWithException(ex);
625 * Getter for property which must exist
627 * @param key Key to get
628 * @return Propety value
630 protected final String getProperty (final String key) {
631 return BaseFrameworkSystem.properties.getProperty(String.format("org.mxchange.addressbook.%s", key)); //NOI18N
635 * Name of used database table, handled over to backend
637 * @return the tableName
639 protected final String getTableName () {
640 return this.tableName;
644 * Name of used database table, handled over to backend
646 * @param tableName the tableName to set
648 protected final void setTableName (final String tableName) {
649 this.tableName = tableName;
653 * Getter for DatabaseFrontend instance
655 * @return DatabaseFrontend instance
657 protected DatabaseFrontend getWrapper () {
662 * Setter for wrapper instance
664 * @param wrapper A DatabaseFrontend instance
666 protected void setWrapper (final DatabaseFrontend wrapper) {
667 this.wrapper = wrapper;
671 * Initializes i18n bundles
673 protected void initBundle () {
674 // Is the bundle set?
675 if (bundle instanceof ResourceBundle) {
677 throw new IllegalStateException("called twice");
681 bundle = ResourceBundle.getBundle(FrameworkInterface.I18N_BUNDLE_FILE); // NOI18N
685 * Checks whether the given field is a boolean field by probing it.
687 * @param instance Instance to call
688 * @param targetClass Target class
689 * @param columnName Column name to check
690 * @return Whether the given column name represents a boolean field
692 protected boolean isBooleanField (final FrameworkInterface instance, final String targetClass, final String columnName) {
694 this.getLogger().trace(MessageFormat.format("instance={0},targetCLass={1},columnName={2} - CALLED!", instance, targetClass, columnName)); //NOI18N
696 // Convert column name to getter name (boolean)
697 String methodName = this.convertColumnNameToGetterMethod(columnName, true);
699 // Get class instance
700 Class<? extends FrameworkInterface> c = this.getClassFromTarget(instance, targetClass);
702 // Defauzlt is boolean
703 boolean isBool = true;
706 // Now try to instance the method
707 Method method = c.getDeclaredMethod(methodName, new Class<?>[0]);
708 } catch (final NoSuchMethodException ex) {
710 this.getLogger().debug(MessageFormat.format("Method {0} does not exist, field {1} cannot be boolean: {2}", methodName, columnName, ex)); //NOI18N
714 } catch (final SecurityException ex) {
716 this.abortProgramWithException(ex);
720 this.getLogger().trace(MessageFormat.format("isBool={0} - EXIT!", isBool)); //NOI18N