Coverage Report - org.crosswire.common.config.AbstractReflectedChoice
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractReflectedChoice
0%
0/100
0%
0/32
3.133
 
 1  
 /**
 2  
  * Distribution License:
 3  
  * JSword is free software; you can redistribute it and/or modify it under
 4  
  * the terms of the GNU Lesser General Public License, version 2.1 or later
 5  
  * as published by the Free Software Foundation. This program is distributed
 6  
  * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 7  
  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 8  
  * See the GNU Lesser General Public License for more details.
 9  
  *
 10  
  * The License is available on the internet at:
 11  
  *       http://www.gnu.org/copyleft/lgpl.html
 12  
  * or by writing to:
 13  
  *      Free Software Foundation, Inc.
 14  
  *      59 Temple Place - Suite 330
 15  
  *      Boston, MA 02111-1307, USA
 16  
  *
 17  
  * © CrossWire Bible Society, 2005 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.common.config;
 21  
 
 22  
 import java.lang.reflect.InvocationTargetException;
 23  
 import java.lang.reflect.Method;
 24  
 import java.util.MissingResourceException;
 25  
 import java.util.ResourceBundle;
 26  
 
 27  
 import org.crosswire.common.util.ClassUtil;
 28  
 import org.crosswire.common.util.StringUtil;
 29  
 import org.crosswire.jsword.JSOtherMsg;
 30  
 import org.jdom2.Element;
 31  
 import org.slf4j.Logger;
 32  
 import org.slf4j.LoggerFactory;
 33  
 
 34  
 /**
 35  
  * A helper for when we need to be a choice created dynamically.
 36  
  * 
 37  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 38  
  * @author Joe Walker
 39  
  * @author DM Smith
 40  
  */
 41  0
 public abstract class AbstractReflectedChoice implements Choice {
 42  
     /*
 43  
      * (non-Javadoc)
 44  
      * 
 45  
      * @see org.crosswire.common.config.Choice#init(org.jdom2.Element)
 46  
      */
 47  
     public void init(Element option, ResourceBundle configResources) throws StartupException {
 48  0
         assert configResources != null;
 49  
 
 50  0
         key = option.getAttributeValue("key");
 51  
 
 52  
         // Hidden is an optional field so it is ok for the resource to be
 53  
         // missing.
 54  
         try {
 55  0
             String hiddenState = configResources.getString(key + ".hidden");
 56  0
             hidden = Boolean.valueOf(hiddenState).booleanValue();
 57  0
         } catch (MissingResourceException e) {
 58  0
             hidden = false;
 59  0
         }
 60  
 
 61  
         // Ignore is an optional field so it is ok for the resource to be
 62  
         // missing.
 63  
         try {
 64  0
             String ignoreState = configResources.getString(key + ".ignore");
 65  0
             ignored = Boolean.valueOf(ignoreState).booleanValue();
 66  0
             if (ignored) {
 67  0
                 hidden = true;
 68  0
                 return;
 69  
             }
 70  0
         } catch (MissingResourceException e) {
 71  0
             ignored = false;
 72  0
         }
 73  
 
 74  0
         String helpText = configResources.getString(key + ".help");
 75  0
         assert helpText != null;
 76  0
         setHelpText(helpText);
 77  
 
 78  
         // OPTIMIZE(dms): This is poorly done (by me!)
 79  0
         String[] pathParts = StringUtil.split(key, '.');
 80  0
         StringBuilder parentKey = new StringBuilder();
 81  0
         StringBuilder path = new StringBuilder();
 82  0
         for (int i = 0; i < pathParts.length; i++) {
 83  0
             if (i > 0) {
 84  0
                 parentKey.append('.');
 85  0
                 path.append('.');
 86  
             }
 87  0
             parentKey.append(pathParts[i]);
 88  0
             String parent = configResources.getString(parentKey + ".name");
 89  0
             assert parent != null;
 90  0
             path.append(parent);
 91  
         }
 92  0
         setFullPath(path.toString());
 93  
 
 94  0
         external = Boolean.valueOf(option.getAttributeValue("external")).booleanValue();
 95  
 
 96  0
         restart = Boolean.valueOf(option.getAttributeValue("restart")).booleanValue();
 97  
 
 98  0
         type = option.getAttributeValue("type");
 99  
 
 100  
         // The important 3 things saying what we update and how we describe
 101  
         // ourselves
 102  0
         Element introspector = option.getChild("introspect");
 103  0
         if (introspector == null) {
 104  0
             throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "introspect"));
 105  
         }
 106  
 
 107  0
         String clazzname = introspector.getAttributeValue("class");
 108  0
         if (clazzname == null) {
 109  0
             throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "class"));
 110  
         }
 111  
 
 112  0
         propertyname = introspector.getAttributeValue("property");
 113  0
         if (propertyname == null) {
 114  0
             throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "property"));
 115  
         }
 116  
 
 117  
         // log.debug("Looking up {}.set{}({} arg0)", clazzname, propertyname, getConvertionClass().getName());
 118  
 
 119  
         try {
 120  0
             clazz = ClassUtil.forName(clazzname);
 121  0
         } catch (ClassNotFoundException ex) {
 122  0
             throw new StartupException(JSOtherMsg.lookupText("Specified class not found: {0}", clazzname), ex);
 123  0
         }
 124  
 
 125  
         try {
 126  0
             setter = clazz.getMethod("set" + propertyname, getConversionClass());
 127  0
         } catch (NoSuchMethodException ex) {
 128  0
             throw new StartupException(JSOtherMsg.lookupText("Specified method not found {0}.set{1}({2} arg0)",
 129  
                     clazz.getName(), propertyname, getConversionClass().getName()), ex
 130  
             );
 131  0
         }
 132  
 
 133  
         try {
 134  
             try {
 135  0
                 getter = clazz.getMethod("is" + propertyname, new Class[0]);
 136  0
             } catch (NoSuchMethodException e) {
 137  0
                 getter = clazz.getMethod("get" + propertyname, new Class[0]);
 138  0
             }
 139  0
         } catch (NoSuchMethodException ex) {
 140  0
             throw new StartupException(JSOtherMsg.lookupText("Specified method not found {0}.get{1}()", clazz.getName(), propertyname), ex);
 141  0
         }
 142  
 
 143  0
         if (getter.getReturnType() != getConversionClass()) {
 144  0
             log.debug("Not using {} from {} because the return type of the getter is not {}", propertyname, clazz.getName(), getConversionClass().getName());
 145  0
             throw new StartupException(JSOtherMsg.lookupText("Mismatch of return types, found: {0} required: {1}", getter.getReturnType(), getConversionClass()));
 146  
         }
 147  0
     }
 148  
 
 149  
     /*
 150  
      * (non-Javadoc)
 151  
      * 
 152  
      * @see org.crosswire.common.config.Choice#getKey()
 153  
      */
 154  
     public String getKey() {
 155  0
         return key;
 156  
     }
 157  
 
 158  
     /*
 159  
      * (non-Javadoc)
 160  
      * 
 161  
      * @see org.crosswire.common.config.Choice#getType()
 162  
      */
 163  
     public String getType() {
 164  0
         return type;
 165  
     }
 166  
 
 167  
     /**
 168  
      * Convert from a reflection return value to a String for storage
 169  
      * 
 170  
      * @param orig the object to be converted to a string
 171  
      * @return the marshaled representation of the object
 172  
      */
 173  
     public abstract String convertToString(Object orig);
 174  
 
 175  
     /**
 176  
      * Convert from a stored string to an object to use with reflection
 177  
      * @param orig the marshaled representation of the object
 178  
      * @return the reconstituted object
 179  
      */
 180  
     public abstract Object convertToObject(String orig);
 181  
 
 182  
     /*
 183  
      * (non-Javadoc)
 184  
      * 
 185  
      * @see org.crosswire.common.config.Choice#getFullPath()
 186  
      */
 187  
     public String getFullPath() {
 188  0
         return fullPath;
 189  
     }
 190  
 
 191  
     /*
 192  
      * (non-Javadoc)
 193  
      * 
 194  
      * @see org.crosswire.common.config.Choice#setFullPath(java.lang.String)
 195  
      */
 196  
     public void setFullPath(String newFullPath) {
 197  0
         fullPath = newFullPath;
 198  0
     }
 199  
 
 200  
     /*
 201  
      * (non-Javadoc)
 202  
      * 
 203  
      * @see org.crosswire.common.config.Choice#getHelpText()
 204  
      */
 205  
     public String getHelpText() {
 206  0
         return helptext;
 207  
     }
 208  
 
 209  
     /*
 210  
      * (non-Javadoc)
 211  
      * 
 212  
      * @see org.crosswire.common.config.Choice#setHelpText(java.lang.String)
 213  
      */
 214  
     public void setHelpText(String helptext) {
 215  0
         this.helptext = helptext;
 216  0
     }
 217  
 
 218  
     /*
 219  
      * (non-Javadoc)
 220  
      * 
 221  
      * @see org.crosswire.common.config.Choice#isSaveable()
 222  
      */
 223  
     public boolean isSaveable() {
 224  0
         return !external;
 225  
     }
 226  
 
 227  
     /*
 228  
      * (non-Javadoc)
 229  
      * 
 230  
      * @see org.crosswire.common.config.Choice#isHidden()
 231  
      */
 232  
     public boolean isHidden() {
 233  0
         return hidden;
 234  
     }
 235  
 
 236  
     /*
 237  
      * (non-Javadoc)
 238  
      * 
 239  
      * @see org.crosswire.common.config.Choice#isIgnored()
 240  
      */
 241  
     public boolean isIgnored() {
 242  0
         return ignored;
 243  
     }
 244  
 
 245  
     /*
 246  
      * (non-Javadoc)
 247  
      * 
 248  
      * @see org.crosswire.common.config.Choice#requiresRestart()
 249  
      */
 250  
     public boolean requiresRestart() {
 251  0
         return restart;
 252  
     }
 253  
 
 254  
     /*
 255  
      * (non-Javadoc)
 256  
      * 
 257  
      * @see org.crosswire.common.config.Choice#getString()
 258  
      */
 259  
     public String getString() {
 260  
         try {
 261  0
             Object retval = getter.invoke(null, new Object[0]);
 262  0
             return convertToString(retval);
 263  0
         } catch (IllegalAccessException ex) {
 264  0
             log.error("Illegal access getting value from {}.{}", clazz.getName(), getter.getName(), ex);
 265  0
             return "";
 266  0
         } catch (InvocationTargetException ex) {
 267  0
             log.error("Failed to get value from {}.{}", clazz.getName(), getter.getName(), ex);
 268  0
             return "";
 269  
         }
 270  
     }
 271  
 
 272  
     /*
 273  
      * (non-Javadoc)
 274  
      * 
 275  
      * @see org.crosswire.common.config.Choice#setString(java.lang.String)
 276  
      */
 277  
     public void setString(String value) throws ConfigException {
 278  0
         Exception ex = null;
 279  
         try {
 280  0
             Object object = convertToObject(value);
 281  0
             if (object != null) {
 282  0
                 setter.invoke(null, object);
 283  
             }
 284  0
         } catch (InvocationTargetException e) {
 285  0
             ex = e;
 286  0
         } catch (IllegalArgumentException e) {
 287  0
             ex = e;
 288  0
         } catch (IllegalAccessException e) {
 289  0
             ex = e;
 290  0
         } catch (NullPointerException e) {
 291  0
             ex = e;
 292  0
         }
 293  
 
 294  0
         if (ex != null) {
 295  0
             log.info("Exception while attempting to execute: {}", setter.toString());
 296  
 
 297  
             // So we can't re-throw the original exception because it wasn't an
 298  
             // Exception so we will have to re-throw the
 299  
             // InvocationTargetException
 300  0
             throw new ConfigException(JSOtherMsg.lookupText("Failed to set option: {0}", setter), ex);
 301  
         }
 302  0
     }
 303  
 
 304  
     /**
 305  
      * The key of the option.
 306  
      */
 307  
     private String key;
 308  
 
 309  
     /**
 310  
      * The type that we reflect to
 311  
      */
 312  
     private Class<? extends Object> clazz;
 313  
 
 314  
     /**
 315  
      * The property that we call on the reflecting class
 316  
      */
 317  
     private String propertyname;
 318  
 
 319  
     /**
 320  
      * The type (as specified in config.xml)
 321  
      */
 322  
     private String type;
 323  
 
 324  
     /**
 325  
      * The method to call to get the value
 326  
      */
 327  
     private Method getter;
 328  
 
 329  
     /**
 330  
      * The method to call to set the value
 331  
      */
 332  
     private Method setter;
 333  
 
 334  
     /**
 335  
      * The help text (tooltip) for this item
 336  
      */
 337  
     private String helptext;
 338  
 
 339  
     /**
 340  
      * The full path of this item
 341  
      */
 342  
     private String fullPath;
 343  
 
 344  
     /**
 345  
      * Whether this choice should be visible or hidden
 346  
      */
 347  
     private boolean hidden;
 348  
 
 349  
     /**
 350  
      * Whether this choice should be ignored altogether.
 351  
      */
 352  
     private boolean ignored;
 353  
 
 354  
     /**
 355  
      * Whether this choice is managed externally, via setXXX and getXXX.
 356  
      */
 357  
     private boolean external;
 358  
 
 359  
     /**
 360  
      * Whether this choice is requires a restart to be seen.
 361  
      */
 362  
     private boolean restart;
 363  
 
 364  
     /**
 365  
      * The log stream
 366  
      */
 367  0
     private static final Logger log = LoggerFactory.getLogger(AbstractReflectedChoice.class);
 368  
 }