Coverage Report - net.objectlab.qalab.parser.BuildStatMoverHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
BuildStatMoverHandler
90%
179/199
100%
26/26
2.611
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 //
 3  
 //                  ObjectLab is sponsoring QALab
 4  
 // 
 5  
 // Based in London, we are world leaders in the design and development 
 6  
 // of bespoke applications for the Securities Financing markets.
 7  
 // 
 8  
 // <a href="http://www.objectlab.co.uk/open">Click here to learn more</a>
 9  
 //           ___  _     _           _   _          _
 10  
 //          / _ \| |__ (_) ___  ___| |_| |    __ _| |__
 11  
 //         | | | | '_ \| |/ _ \/ __| __| |   / _` | '_ \
 12  
 //         | |_| | |_) | |  __/ (__| |_| |__| (_| | |_) |
 13  
 //          \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
 14  
 //                   |__/
 15  
 //
 16  
 //                   http://www.ObjectLab.co.uk
 17  
 // ---------------------------------------------------------------------------
 18  
 //
 19  
 //QALab is released under the GNU General Public License.
 20  
 //
 21  
 //QALab: Collects QA Statistics from your build over time.
 22  
 //2005+, ObjectLab Ltd
 23  
 //
 24  
 //This library is free software; you can redistribute it and/or
 25  
 //modify it under the terms of the GNU General Public
 26  
 //License as published by the Free Software Foundation; either
 27  
 //version 2.1 of the License, or (at your option) any later version.
 28  
 //
 29  
 //This library is distributed in the hope that it will be useful,
 30  
 //but WITHOUT ANY WARRANTY; without even the implied warranty of
 31  
 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 32  
 //General Public License for more details.
 33  
 //
 34  
 //You should have received a copy of the GNU General Public
 35  
 //License along with this library; if not, write to the Free Software
 36  
 //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 37  
 //
 38  
 ////////////////////////////////////////////////////////////////////////////////
 39  
 package net.objectlab.qalab.parser;
 40  
 
 41  
 import net.objectlab.qalab.util.QALabTags;
 42  
 import net.objectlab.qalab.util.TaskLogger;
 43  
 import net.objectlab.qalab.util.Util;
 44  
 
 45  
 import org.xml.sax.Attributes;
 46  
 import org.xml.sax.InputSource;
 47  
 import org.xml.sax.SAXException;
 48  
 import org.xml.sax.helpers.DefaultHandler;
 49  
 
 50  
 import javax.xml.parsers.ParserConfigurationException;
 51  
 import javax.xml.parsers.SAXParser;
 52  
 import javax.xml.parsers.SAXParserFactory;
 53  
 
 54  
 import java.io.IOException;
 55  
 import java.io.Writer;
 56  
 
 57  
 import java.text.ParseException;
 58  
 
 59  
 import java.util.Calendar;
 60  
 import java.util.Date;
 61  
 import java.util.HashMap;
 62  
 import java.util.Iterator;
 63  
 import java.util.LinkedList;
 64  
 import java.util.List;
 65  
 import java.util.Map;
 66  
 import java.util.Stack;
 67  
 
 68  
 /**
 69  
  * A handler class which uses SAX method to parse the XML file from QALab and
 70  
  * extract the files that have seen a change in statistics within the time
 71  
  * window.
 72  
  * 
 73  
  * @author Paramjit Rehinsi
 74  
  * @version $Revision: 199 $
 75  
  * @todo add a StartDate for the window
 76  
  * @todo add an EndDate for the Window
 77  
  * @todo simplify the constructor to remove File dependencies.
 78  
  */
 79  
 public class BuildStatMoverHandler extends DefaultHandler {
 80  
     private static final int ONE_DAY = 24;
 81  
 
 82  
     /**
 83  
      * default 48h to deduct is weekendAdjustment.
 84  
      */
 85  
     private static final int WEEKEND_SKIP = 48;
 86  
 
 87  
     // ~ Instance fields
 88  
     // ------------------------------------------------------------------------
 89  
 
 90  
     /**
 91  
      * The types to be handled checkstyle|findbugs|pmd|simian.
 92  
      */
 93  21
     private List listOfTypes = new LinkedList();
 94  
 
 95  
     /**
 96  
      * The logger to use for exceptions and debug information.
 97  
      */
 98  
     private TaskLogger tasklogger;
 99  
 
 100  
     /**
 101  
      * The QALab input source.
 102  
      */
 103  
     private InputSource qalabSource;
 104  
 
 105  
     /**
 106  
      * the XML writer with the "movers".
 107  
      */
 108  
     private Writer outputWriter;
 109  
 
 110  
     /**
 111  
      * A map containing a Stack of items per type.
 112  
      */
 113  21
     private Map mapOfTypes = new HashMap();
 114  
 
 115  
     /**
 116  
      * list of file results.
 117  
      */
 118  21
     private List filebeans = new LinkedList();
 119  
 
 120  
     /**
 121  
      * the current file result.
 122  
      */
 123  
     private FileStats fileResult;
 124  
 
 125  
     /**
 126  
      * the current file name.
 127  
      */
 128  
     private String filename;
 129  
 
 130  
     /**
 131  
      * the current path to file.
 132  
      */
 133  
     private String path;
 134  
 
 135  
     /**
 136  
      * cutoff earliest date to consider in the stats.
 137  
      */
 138  
     private Date startDateCutoff;
 139  
 
 140  
     /**
 141  
      * cutoff latest date to consider in the stats.
 142  
      */
 143  
     private Date endDateCutoff;
 144  
 
 145  
     /**
 146  
      * StartTimeWindow.
 147  
      */
 148  21
     private Date startTimeWindow = null;
 149  
 
 150  
     /**
 151  
      * End Time Window.
 152  
      */
 153  21
     private Date endTimeWindow = null;
 154  
 
 155  
     /**
 156  
      * start time offset (from now or endTimeWindow).
 157  
      */
 158  
     private String offsetHours;
 159  
 
 160  
     /**
 161  
      * weekendAdjustment, true and an extra 48 will be subtracted to any
 162  
      * datecutoff that fell on a Saturday or Sunday.
 163  
      */
 164  21
     private boolean wkendadj = false;
 165  
 
 166  
     /**
 167  
      * quiet no debug if true (default).
 168  
      */
 169  21
     private boolean quiet = true;
 170  
     
 171  
     /** offset in hours to determine if the stat has fallen to 0 over the last n hours
 172  
      * from the endDateCutoff.
 173  
      */
 174  21
     private int hoursOffsetForLastRun = ONE_DAY;
 175  
 
 176  
     // ~ Constructors
 177  
     // ------------------------------------------------------------------------
 178  
 
 179  
     /**
 180  
      * Constructor used by ant Task/Maven.
 181  
      * 
 182  
      * @param qaLabSource
 183  
      *            the original QALab file.
 184  
      * @param types
 185  
      *            the types to extract.
 186  
      * @param outputFile
 187  
      *            the output file.
 188  
      * @param weekendAdjustment
 189  
      *            true if it should adjust for weekends.
 190  
      * @param log
 191  
      *            the logger to use.
 192  
      * @param noDebug
 193  
      *            if true, quiet output (no debug info).
 194  
      */
 195  
     public BuildStatMoverHandler(final InputSource qaLabSource,
 196  
             final String types, final Writer outputFile,
 197  
             final boolean weekendAdjustment, final TaskLogger log,
 198  21
             final boolean noDebug) {
 199  21
         setLogger(log);
 200  21
         quiet = noDebug;
 201  21
         wkendadj = weekendAdjustment;
 202  21
         listOfTypes = Util.listify(types, ",");
 203  21
         processTypes(listOfTypes);
 204  21
         this.outputWriter = outputFile;
 205  
 
 206  21
         qalabSource = qaLabSource;
 207  21
     }
 208  
 
 209  
     // ~ Methods
 210  
     // ------------------------------------------------------------------------
 211  
 
 212  
     /**
 213  
      * Create an instance of the SAX Parser and process the qalab.xml file.
 214  
      * 
 215  
      * @throws ParserConfigurationException
 216  
      *             cannot create an instance of parser.
 217  
      * @throws SAXException
 218  
      *             any SAX issue.
 219  
      * @throws java.io.IOException
 220  
      *             any IO/access to file issue.
 221  
      */
 222  
     public final void process() throws ParserConfigurationException,
 223  
             SAXException, IOException {
 224  21
         calculateCutoffDateTime();
 225  
 
 226  21
         SAXParserFactory factory = SAXParserFactory.newInstance();
 227  21
         SAXParser saxParser = factory.newSAXParser();
 228  
 
 229  21
         saxParser.parse(getQalabSource(), this);
 230  21
     }
 231  
 
 232  
     /**
 233  
      * Process the types, assign a new stack to each type.
 234  
      * 
 235  
      * @param listoftypes
 236  
      *            list of types to support.
 237  
      */
 238  
     private void processTypes(final List listoftypes) {
 239  273
         Iterator i = listoftypes.iterator();
 240  
 
 241  588
         while (i.hasNext()) {
 242  315
             mapOfTypes.put(i.next(), new Stack());
 243  315
         }
 244  273
     }
 245  
 
 246  
     /**
 247  
      * Gets the filename from the id attribute.
 248  
      * 
 249  
      * @param attrs
 250  
      *            XML attributes.
 251  
      */
 252  
     public final void getFileName(final Attributes attrs) {
 253  252
         if (attrs != null) {
 254  756
             for (int i = 0; i < attrs.getLength(); i++) {
 255  504
                 String name = attrs.getQName(i); // Attr name
 256  504
                 String value = attrs.getValue(i);
 257  
 
 258  504
                 if (QALabTags.ID_ATTRIBUTE.equals(name)) {
 259  252
                     filename = value;
 260  
 
 261  252
                     if (!quiet) {
 262  252
                         tasklogger.log("File:" + filename);
 263  252
                     }
 264  252
                 } else if (QALabTags.PATH_ATTRIBUTE.equals(name)) {
 265  252
                     path = value;
 266  
                 }
 267  
             }
 268  
         }
 269  252
     }
 270  
 
 271  
     /**
 272  
      * Process the attributes of each result and return a SingleStat object
 273  
      * built from the attributes date, type and statvalue.
 274  
      * 
 275  
      * @param attrs
 276  
      *            XML attributes
 277  
      * @return new SingleStat object built from the attributes.
 278  
      */
 279  
     private SingleStat buildResultBean(final Attributes attrs) {
 280  996
         SingleStat singleStat = new SingleStat();
 281  
 
 282  996
         if (attrs != null) {
 283  3984
             for (int i = 0; i < attrs.getLength(); i++) {
 284  2988
                 String name = attrs.getQName(i); // Attr name
 285  2988
                 String value = attrs.getValue(i);
 286  
 
 287  2988
                 if (QALabTags.TYPE_ATTRIBUTE.equals(name)) {
 288  996
                     singleStat.setType(value);
 289  996
                 } else if (QALabTags.STATVALUE_ATTRIBUTE.equals(name)) {
 290  996
                     singleStat.setStatValue(Integer.parseInt(value));
 291  996
                 } else if (QALabTags.DATE_ATTRIBUTE.equals(name)) {
 292  
                     try {
 293  996
                         if (value != null
 294  
                                 && value.length() > QALabTags.DATE_ONLY_SIZE) {
 295  996
                             singleStat
 296  
                                     .setDate(QALabTags.DEFAULT_DATETIME_FORMAT
 297  
                                             .parse(value));
 298  996
                         } else if (value != null
 299  
                                 && value.length() == QALabTags.DATE_ONLY_SIZE) {
 300  0
                             singleStat.setDate(QALabTags.DEFAULT_DATE_FORMAT
 301  
                                     .parse(value));
 302  
                         }
 303  0
                     } catch (ParseException e) {
 304  0
                         tasklogger.log(e.toString());
 305  996
                     }
 306  
                 }
 307  
             }
 308  
         }
 309  
 
 310  996
         return singleStat;
 311  
     }
 312  
 
 313  
     // ===========================================================
 314  
     // SAX DocumentHandler methods
 315  
     // ===========================================================
 316  
 
 317  
     /**
 318  
      * At the start of a new element, capture the filename and if the element is
 319  
      * a result one, create a SingleStat to store for the given type.
 320  
      * 
 321  
      * @param ignoreNamespaceURI
 322  
      *            ignore (present for interface implementation).
 323  
      * @param localname
 324  
      *            name of the current element.
 325  
      * @param qualifiedname
 326  
      *            element name.
 327  
      * @param attrs
 328  
      *            the XML attribute of the current element.
 329  
      * @throws SAXException
 330  
      *             any SAX issue
 331  
      */
 332  
     public final void startElement(final String ignoreNamespaceURI, // NOPMD
 333  
             final String localname, final String qualifiedname,
 334  
             final Attributes attrs) throws SAXException {
 335  2292
         String local = localname;
 336  2292
         if ("".equals(local)) {
 337  2292
             local = qualifiedname;
 338  
         }
 339  
 
 340  
         // if (!quiet) {
 341  
         // tasklogger.log("Start :" + qualifiedname);
 342  
         // }
 343  
 
 344  2292
         if (QALabTags.FILE_TAG.equals(local)) {
 345  252
             getFileName(attrs);
 346  
         }
 347  
 
 348  2292
         if (QALabTags.RESULT_TAG.equals(local)) {
 349  996
             SingleStat resultbean = buildResultBean(attrs);
 350  996
             Stack s = (Stack) mapOfTypes.get(resultbean.getType());
 351  
 
 352  
             // only push items that are within the time window.
 353  996
             if (s != null) {
 354  624
                 s.push(resultbean);
 355  
             }
 356  
         }
 357  2292
     }
 358  
 
 359  
     /**
 360  
      * At the end of an element, check if it is a file one and add the results
 361  
      * found.
 362  
      * 
 363  
      * @param ignoreNamespaceURI
 364  
      *            ignore (present for interface implementation).
 365  
      * @param ignoreSimplename
 366  
      *            ignore (present for interface implementation).
 367  
      * @param qualifiedname
 368  
      *            the name of the element.
 369  
      * @throws SAXException
 370  
      *             any SAX issue
 371  
      */
 372  
     public final void endElement(final String ignoreNamespaceURI, // NOPMD
 373  
             final String ignoreSimplename, final String qualifiedname) // NOPMD
 374  
             throws SAXException {
 375  2292
         if (QALabTags.FILE_TAG.equals(qualifiedname)) {
 376  252
             fileResult = new FileStats(mapOfTypes, filename, path,
 377  
                     startDateCutoff);
 378  252
             fileResult.setEndDateCutoff(endDateCutoff);
 379  252
             fileResult.setOffsetForLastRun(hoursOffsetForLastRun);
 380  252
             filebeans.add(fileResult);
 381  252
             mapOfTypes = new HashMap();
 382  252
             processTypes(listOfTypes);
 383  
         }
 384  2292
     }
 385  
 
 386  
     /**
 387  
      * Return the input source to the base stats.
 388  
      * 
 389  
      * @return InputSource typically the source to qalab.xml
 390  
      */
 391  
     public final InputSource getQalabSource() {
 392  21
         return qalabSource;
 393  
     }
 394  
 
 395  
     /**
 396  
      * At the end of parsing the qalab.xml document, generate the up/down
 397  
      * report.
 398  
      * 
 399  
      * @throws SAXException
 400  
      *             any SAX issue.
 401  
      */
 402  
     public final void endDocument() throws SAXException {
 403  
         try {
 404  
             // now write the results
 405  21
             outputWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
 406  21
             outputWriter.write(QALabTags.LINE_END);
 407  21
             outputWriter.write("<!DOCTYPE moversreport [");
 408  21
             outputWriter.write(QALabTags.LINE_END);
 409  21
             outputWriter.write("<!ELEMENT moversreport (types,datethreshold,"
 410  
                     + "enddatethreshold,daterun,up?,down?)>");
 411  21
             outputWriter.write(QALabTags.LINE_END);
 412  21
             outputWriter.write("<!ELEMENT types (#PCDATA)>");
 413  21
             outputWriter.write(QALabTags.LINE_END);
 414  21
             outputWriter.write("<!ELEMENT datethreshold (#PCDATA)>");
 415  21
             outputWriter.write(QALabTags.LINE_END);
 416  21
             outputWriter.write("<!ELEMENT enddatethreshold (#PCDATA)>");
 417  21
             outputWriter.write(QALabTags.LINE_END);
 418  21
             outputWriter.write("<!ELEMENT daterun (#PCDATA)>");
 419  21
             outputWriter.write(QALabTags.LINE_END);
 420  21
             outputWriter.write("<!ELEMENT up (file+)>");
 421  21
             outputWriter.write(QALabTags.LINE_END);
 422  21
             outputWriter.write("<!ELEMENT down (file+)>");
 423  21
             outputWriter.write(QALabTags.LINE_END);
 424  21
             outputWriter.write("<!ELEMENT file (diff+)>");
 425  21
             outputWriter.write(QALabTags.LINE_END);
 426  21
             outputWriter.write("<!ATTLIST file name CDATA #REQUIRED>");
 427  21
             outputWriter.write(QALabTags.LINE_END);
 428  21
             outputWriter.write("<!ATTLIST file path CDATA #REQUIRED>");
 429  21
             outputWriter.write(QALabTags.LINE_END);
 430  21
             outputWriter.write("<!ELEMENT diff EMPTY>");
 431  21
             outputWriter.write(QALabTags.LINE_END);
 432  21
             outputWriter.write("<!ATTLIST diff previousrun CDATA #REQUIRED>");
 433  21
             outputWriter.write(QALabTags.LINE_END);
 434  21
             outputWriter.write("<!ATTLIST diff currentrun CDATA #REQUIRED>");
 435  21
             outputWriter.write(QALabTags.LINE_END);
 436  21
             outputWriter.write("<!ATTLIST diff type CDATA #REQUIRED>");
 437  21
             outputWriter.write(QALabTags.LINE_END);
 438  21
             outputWriter.write("<!ATTLIST diff previouserrors "
 439  
                     + "CDATA #REQUIRED>");
 440  21
             outputWriter.write(QALabTags.LINE_END);
 441  21
             outputWriter.write("<!ATTLIST diff currenterrors CDATA "
 442  
                     + "#REQUIRED>");
 443  21
             outputWriter.write(QALabTags.LINE_END);
 444  21
             outputWriter.write("<!ATTLIST diff diff CDATA #REQUIRED>");
 445  21
             outputWriter.write(QALabTags.LINE_END);
 446  21
             outputWriter.write("]>");
 447  21
             outputWriter.write(QALabTags.LINE_END);
 448  21
             outputWriter.write("<moversreport>");
 449  21
             outputWriter.write(QALabTags.LINE_END);
 450  21
             outputWriter.write(" <types>");
 451  21
             outputWriter.write(Util.listToCSVString(listOfTypes));
 452  21
             outputWriter.write("</types>");
 453  21
             outputWriter.write(QALabTags.LINE_END);
 454  21
             outputWriter.write(" <datethreshold>");
 455  21
             outputWriter.write(QALabTags.DEFAULT_DATETIME_FORMAT
 456  
                     .format(startDateCutoff));
 457  21
             outputWriter.write("</datethreshold>");
 458  21
             outputWriter.write(QALabTags.LINE_END);
 459  21
             outputWriter.write(" <enddatethreshold>");
 460  21
             outputWriter.write(QALabTags.DEFAULT_DATETIME_FORMAT
 461  
                     .format(endDateCutoff));
 462  21
             outputWriter.write("</enddatethreshold>");
 463  21
             outputWriter.write(QALabTags.LINE_END);
 464  21
             outputWriter.write(" <daterun>");
 465  21
             outputWriter.write(QALabTags.DEFAULT_DATETIME_FORMAT
 466  
                     .format(new Date()));
 467  21
             outputWriter.write("</daterun>");
 468  21
             outputWriter.write(QALabTags.LINE_END);
 469  21
             outputWriter.write(" <up>");
 470  21
             outputWriter.write(QALabTags.LINE_END);
 471  
 
 472  21
             for (Iterator it = filebeans.iterator(); it.hasNext();) {
 473  252
                 FileStats fb = (FileStats) it.next();
 474  
 
 475  252
                 fb.constructXML();
 476  252
                 outputWriter.write(fb.getUpXmlResult());
 477  252
             }
 478  
 
 479  21
             outputWriter.write(" </up>");
 480  21
             outputWriter.write(QALabTags.LINE_END);
 481  21
             outputWriter.write(" <down>");
 482  21
             outputWriter.write(QALabTags.LINE_END);
 483  
 
 484  21
             for (Iterator it = filebeans.iterator(); it.hasNext();) {
 485  252
                 FileStats fb = (FileStats) it.next();
 486  
 
 487  252
                 outputWriter.write(fb.getDownXmlResult());
 488  252
             }
 489  
 
 490  21
             outputWriter.write(" </down>");
 491  21
             outputWriter.write(QALabTags.LINE_END);
 492  21
             outputWriter.write("</moversreport>");
 493  
 
 494  21
             outputWriter.flush();
 495  0
         } catch (IOException e) {
 496  0
             tasklogger.log(e.toString());
 497  
         } finally {
 498  21
             if (outputWriter != null) {
 499  
                 try {
 500  21
                     outputWriter.close();
 501  0
                 } catch (IOException e) {
 502  0
                     tasklogger.log(e.toString());
 503  21
                 }
 504  0
             }
 505  0
         }
 506  21
     }
 507  
 
 508  
     /**
 509  
      * calculates the datecutoff by adding the number of offset hours.
 510  
      * 
 511  
      * @param offsetInHours
 512  
      *            number of hours from now.
 513  
      */
 514  
     public final void setDatecutoff(final int offsetInHours) {
 515  0
         Calendar cal = Calendar.getInstance();
 516  
 
 517  0
         cal.add(Calendar.HOUR, offsetInHours);
 518  0
         startDateCutoff = cal.getTime();
 519  0
     }
 520  
 
 521  
     /**
 522  
      * Calculate the cutoff date, ie the earliest date for the stat to be taken
 523  
      * into account. extra 48h if original cutoff date falls on a Saturday or
 524  
      * Sunday.
 525  
      */
 526  
     public final void calculateCutoffDateTime() {
 527  21
         if (startTimeWindow != null) {
 528  21
             startDateCutoff = startTimeWindow;
 529  21
         } else {
 530  
             // always do the cut off
 531  0
             int offset = -Integer.parseInt(offsetHours);
 532  
 
 533  0
             setDatecutoff(offset);
 534  
         }
 535  
 
 536  21
         if (wkendadj) {
 537  
             // check if weekend:
 538  3
             Calendar cal = Calendar.getInstance();
 539  
 
 540  3
             cal.setTime(startDateCutoff);
 541  
 
 542  3
             final int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
 543  
 
 544  3
             if ((dayOfWeek == Calendar.SUNDAY)
 545  
                     || (dayOfWeek == Calendar.SATURDAY)) {
 546  3
                 cal.add(Calendar.HOUR, -WEEKEND_SKIP);
 547  3
                 startDateCutoff = cal.getTime();
 548  
             }
 549  
         }
 550  
 
 551  21
         if (!quiet) {
 552  21
             tasklogger.log("Cutoff = " + startDateCutoff);
 553  
         }
 554  
 
 555  21
         if (endTimeWindow != null) {
 556  21
             endDateCutoff = endTimeWindow;
 557  21
         } else {
 558  0
             endDateCutoff = new Date();
 559  
         }
 560  21
     }
 561  
 
 562  
     /**
 563  
      * @param log
 564  
      *            the logger to use.
 565  
      */
 566  
     private void setLogger(final TaskLogger log) {
 567  21
         this.tasklogger = log;
 568  21
     }
 569  
 
 570  
     /**
 571  
      * @param endTime
 572  
      *            The endTimeWindow to set.
 573  
      */
 574  
     public final void setEndTimeWindow(final Date endTime) {
 575  21
         this.endTimeWindow = new Date(endTime.getTime());
 576  21
     }
 577  
 
 578  
     /**
 579  
      * @param startTime
 580  
      *            The startTimeWindow to set.
 581  
      */
 582  
     public final void setStartTimeWindow(final Date startTime) {
 583  21
         this.startTimeWindow = new Date(startTime.getTime());
 584  21
     }
 585  
 
 586  
     /**
 587  
      * @param offset
 588  
      *            The offsetHours to set.
 589  
      */
 590  
     public final void setOffsetHours(final String offset) {
 591  0
         this.offsetHours = offset;
 592  0
     }
 593  
 
 594  
     /**
 595  
      * @return Returns the offsetHours.
 596  
      */
 597  
     public final String getOffsetHours() {
 598  0
         return offsetHours;
 599  
     }
 600  
 
 601  
     public int getHoursOffsetForLastRun() {
 602  0
         return hoursOffsetForLastRun;
 603  
     }
 604  
 
 605  
     public void setHoursOffsetForLastRun(int hoursOffsetForLastRun) {
 606  3
         this.hoursOffsetForLastRun = hoursOffsetForLastRun;
 607  3
     }
 608  
 }
 609  
 /*
 610  
  *                   ObjectLab is sponsoring QALab
 611  
  * 
 612  
  * Based in London, we are world leaders in the design and development 
 613  
  * of bespoke applications for the securities financing markets.
 614  
  * 
 615  
  * <a href="http://www.objectlab.co.uk/open">Click here to learn more about us</a>
 616  
  *           ___  _     _           _   _          _
 617  
  *          / _ \| |__ (_) ___  ___| |_| |    __ _| |__
 618  
  *         | | | | '_ \| |/ _ \/ __| __| |   / _` | '_ \
 619  
  *         | |_| | |_) | |  __/ (__| |_| |__| (_| | |_) |
 620  
  *          \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
 621  
  *                   |__/
 622  
  *
 623  
  *                     www.ObjectLab.co.uk
 624  
  */