nysenate$openlegislation
String
String schema()
String
String getEnvSchema()
nysenate$openlegislation
String
String getAmendmentVersion()
String
String getBillAmendment()
nysenate$openlegislation
SOBIFragmentType
SOBIFragmentType getType()
SOBIFragmentType
SOBIFragmentType getFragmentType()
nysenate$openlegislation
String
String getBaseBillPrintNo()
String
String getOid()
nysenate$openlegislation
String
String getFragmentFileName()
String
String getPrintNo()
nysenate$openlegislation
String amendmentVersion
void
void setAmendmentVersion(String amendmentVersion)
String billVersion
void
void setBillAmendment(String billVersion)
nysenate$openlegislation
String printNo
void
void setPrintNo(String printNo)
String billId
void
void setBillId(String billId)
nysenate$openlegislation
String baseBillPrintNo
this.baseBillPrintNo = baseBillPrintNo;
void
void setBaseBillPrintNo(String baseBillPrintNo)
String oid
this.oid = oid;
    /**
     * @return - The date of the action.
     */
    public Date getDate() {
        return date;
void
void setOid(String oid)
nysenate$openlegislation
String fileName
void
void setFileName(String fileName)
String text
void
void setText(String text)
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2FEnvironment.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2FEnvironment.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/Environment.java
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FileUtils;

@@ -1,13 +1,12 @@
 package gov.nysenate.openleg;
 
 import gov.nysenate.util.Config;
+import org.apache.commons.io.FileUtils;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.Date;
 
-import org.apache.commons.io.FileUtils;
-
 /**
  * The Environment class is essentially a configuration for an OpenLeg workspace.
  * Each workspace contains its own set of directories to store the source data files
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2FEnvironment.java
2a9b53be49311cf6a5be8b499ce01dc56f4e14c0
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2FAbstractApiRequest.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2FAbstractApiRequest.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/AbstractApiRequest.java
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@@ -4,15 +4,14 @@
 import gov.nysenate.openleg.util.OpenLegConstants;
 import gov.nysenate.openleg.util.TextFormatter;
 
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 /*
  * abstract wrapper for api request objects
  */
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2FAbstractApiRequest.java
6954875a25d895fa72eff3d7347ff0826034978c
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2FApiType.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2FApiType.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/ApiType.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.bill.BillAction;
import gov.nysenate.openleg.model.bill.Vote;
import gov.nysenate.openleg.model.calendar.Calendar;
import gov.nysenate.openleg.model.transcript.Transcript;
import gov.nysenate.openleg.model.BillAction;
import gov.nysenate.openleg.model.Bill;
import gov.nysenate.openleg.model.Calendar;
import gov.nysenate.openleg.model.Transcript;
import gov.nysenate.openleg.model.Vote;
@@ -1,11 +1,11 @@
 package gov.nysenate.openleg.api;
 
-import gov.nysenate.openleg.model.BillAction;
 import gov.nysenate.openleg.model.BaseObject;
-import gov.nysenate.openleg.model.Bill;
-import gov.nysenate.openleg.model.Calendar;
-import gov.nysenate.openleg.model.Transcript;
-import gov.nysenate.openleg.model.Vote;
+import gov.nysenate.openleg.model.bill.Bill;
+import gov.nysenate.openleg.model.bill.BillAction;
+import gov.nysenate.openleg.model.bill.Vote;
+import gov.nysenate.openleg.model.calendar.Calendar;
+import gov.nysenate.openleg.model.transcript.Transcript;
 
 public enum ApiType {
     BILL		("bill", 		Bill.class),
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2FApiType.java
a347f5300f471bd92c2a76e7babdb29d5c61fa56
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FAdvancedSearchServlet.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FAdvancedSearchServlet.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/servlets/AdvancedSearchServlet.java
import org.apache.commons.io.FileUtils;
import org.codehaus.jackson.map.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.codehaus.jackson.map.ObjectMapper;

@@ -3,21 +3,19 @@
 import gov.nysenate.openleg.util.SessionYear;
 import gov.nysenate.services.model.Committee;
 import gov.nysenate.services.model.Senator;
+import org.apache.commons.io.FileUtils;
+import org.codehaus.jackson.map.ObjectMapper;
 
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.io.FileUtils;
-import org.codehaus.jackson.map.ObjectMapper;
-
 @SuppressWarnings("serial")
 public class AdvancedSearchServlet extends HttpServlet
 {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FAdvancedSearchServlet.java
b97f787e98d0616c5eefca86adfcfa8d6339ca65
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FCommitteesServlet.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FCommitteesServlet.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/servlets/CommitteesServlet.java
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;

@@ -5,7 +5,14 @@
 import gov.nysenate.openleg.util.SessionYear;
 import gov.nysenate.services.model.Committee;
 import gov.nysenate.services.model.Member;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+import org.codehaus.jackson.map.ObjectMapper;
 
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -14,15 +21,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.log4j.Logger;
-import org.codehaus.jackson.map.ObjectMapper;
-
 @SuppressWarnings("serial")
 public class CommitteesServlet extends HttpServlet
 {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FCommitteesServlet.java
88bb4a0873263246b14a11fea25ab1a248e9458e
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FLogFilter.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FLogFilter.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/servlets/LogFilter.java
import org.apache.log4j.Logger;
import javax.servlet.*;
import java.io.IOException;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.log4j.Logger;
@@ -1,16 +1,10 @@
 package gov.nysenate.openleg.api.servlets;
 
-import java.io.IOException;
+import org.apache.log4j.Logger;
 
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
+import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
-
-import org.apache.log4j.Logger;
+import java.io.IOException;
 
 public class LogFilter implements Filter
 {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FLogFilter.java
18be339c97c6322798cf8b7ac25ec20c3b490fa9
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FRulesServlet.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FRulesServlet.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/servlets/RulesServlet.java
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
@@ -1,16 +1,14 @@
 package gov.nysenate.openleg.api.servlets;
 
 import gov.nysenate.openleg.util.Application;
-
-import java.io.File;
-import java.io.IOException;
+import org.apache.commons.io.FileUtils;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.io.FileUtils;
+import java.io.File;
+import java.io.IOException;
 
 @SuppressWarnings("serial")
 public class RulesServlet extends HttpServlet {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FRulesServlet.java
b96420d1b2bcf19cef2b5c7fde0a751d3bbe78f8
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FSenatorsServlet.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FSenatorsServlet.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/servlets/SenatorsServlet.java
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;

@@ -4,7 +4,14 @@
 import gov.nysenate.openleg.util.RequestUtils.FORMAT;
 import gov.nysenate.openleg.util.SessionYear;
 import gov.nysenate.services.model.Senator;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+import org.codehaus.jackson.map.ObjectMapper;
 
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -13,15 +20,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.log4j.Logger;
-import org.codehaus.jackson.map.ObjectMapper;
-
 @SuppressWarnings("serial")
 public class SenatorsServlet extends HttpServlet
 {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2FSenatorsServlet.java
e9482e31df78b4534e0375bd6617bd3a8ce6b48a
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2Fadmin%2FReportsServlet.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2Fadmin%2FReportsServlet.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/servlets/admin/ReportsServlet.java
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.log4j.Logger;
@@ -4,21 +4,19 @@
 import gov.nysenate.openleg.model.admin.ReportDAO;
 import gov.nysenate.openleg.model.admin.ReportError;
 import gov.nysenate.openleg.model.admin.ReportObservation;
-
-import java.io.IOException;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
+import org.apache.commons.dbutils.QueryRunner;
+import org.apache.commons.dbutils.handlers.BeanHandler;
+import org.apache.commons.dbutils.handlers.BeanListHandler;
+import org.apache.log4j.Logger;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.dbutils.QueryRunner;
-import org.apache.commons.dbutils.handlers.BeanHandler;
-import org.apache.commons.dbutils.handlers.BeanListHandler;
-import org.apache.log4j.Logger;
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Servlet implementation class ErrorLog
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2Fadmin%2FReportsServlet.java
c9d4f215f20f06520c5f4edcbe3a819f45a813e2
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2Fadmin%2FUpdatesServlet.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2Fadmin%2FUpdatesServlet.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/api/servlets/admin/UpdatesServlet.java
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.log4j.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TreeMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.log4j.Logger;
@@ -3,28 +3,20 @@
 import gov.nysenate.openleg.model.Change;
 import gov.nysenate.openleg.util.Application;
 import gov.nysenate.openleg.util.Storage;
+import org.apache.commons.dbutils.QueryRunner;
+import org.apache.commons.dbutils.ResultSetHandler;
+import org.apache.log4j.Logger;
 
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.TreeMap;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.dbutils.QueryRunner;
-import org.apache.commons.dbutils.ResultSetHandler;
-import org.apache.log4j.Logger;
+import java.util.*;
 
 /**
  * Servlet implementation class UpdateServlet
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fapi%2Fservlets%2Fadmin%2FUpdatesServlet.java
6088e72a4c4b82defc170da011c92763fd0dcb25
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fbase%2FSqlBaseDao.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fbase%2FSqlBaseDao.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/dao/base/SqlBaseDao.java
import java.util.List;
    /** --- Table Name Definitions --- */

    protected static String BILL_TABLE = "bill";
    protected static String BILL_AMENDEMENT_TABLE = "bill_amendment";
    protected static String BILL_AMENDMENT_ACTION_TABLE = "bill_amendment_action";
    protected static String BILL_AMENDMENT_COSPONSOR_TABLE = "bill_amendment_cosponsor";
    protected static String BILL_AMENDMENT_VOTE = "bill_amendment_vote";

    protected String schema() {
        return schema() + "." + tableName;
    /**
     * Outputs the ORDER BY clause for multiple column name and sort order pairs.
     * @param columnNames List<String>
     * @param sortOrders List<SortOrder>>
     * @return String
     */
    protected String orderBy(List<String> columnNames, List<SortOrder> sortOrders) {
        if (columnNames.size() != sortOrders.size()) {
            throw new IllegalArgumentException("For order by clause, number of column names must match sort orders!");
        }
        String orderByClause = "ORDER BY ";
        for (int i = 0; i < columnNames.size(); i++) {
            if (sortOrders.get(i) != null && !sortOrders.get(i).equals(SortOrder.NONE)) {
                orderByClause += (columnNames.get(i) + " " + sortOrders.get(i).name() + ((i + 1 == columnNames.size()) ? " " : ", "));
            }
        }
        return orderByClause;
    }

src/main/java/gov/nysenate/openleg/dao/base/SqlBaseDao.java
return schema() + "." + tableName;
    /**
     * Outputs the ORDER BY clause for multiple column name and sort order pairs.
     * @param columnNames List<String>
     * @param sortOrders List<SortOrder>>
     * @return String
     */
    protected String orderBy(List<String> columnNames, List<SortOrder> sortOrders) {
        if (columnNames.size() != sortOrders.size()) {
            throw new IllegalArgumentException("For order by clause, number of column names must match sort orders!");
String schema()
import org.apache.commons.dbutils.QueryRunner;
    protected String getEnvSchema() {
        return getEnvSchema() + "." + tableName;
@@ -2,12 +2,12 @@
 
 import gov.nysenate.openleg.Environment;
 import gov.nysenate.openleg.util.Application;
-import org.apache.commons.dbutils.QueryRunner;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
 
 import java.sql.Timestamp;
 import java.util.Date;
+import java.util.List;
 
 /**
  * Base class for SQL data access layer classes to inherit common functionality from.
@@ -26,6 +26,14 @@ public abstract class SqlBaseDao
     /** Reference to the environment in which the data is stored */
     protected Environment environment;
 
+    /** --- Table Name Definitions --- */
+
+    protected static String BILL_TABLE = "bill";
+    protected static String BILL_AMENDEMENT_TABLE = "bill_amendment";
+    protected static String BILL_AMENDMENT_ACTION_TABLE = "bill_amendment_action";
+    protected static String BILL_AMENDMENT_COSPONSOR_TABLE = "bill_amendment_cosponsor";
+    protected static String BILL_AMENDMENT_VOTE = "bill_amendment_vote";
+
     protected SqlBaseDao(Environment environment) {
         this.environment = environment;
     }
@@ -34,7 +42,7 @@ protected SqlBaseDao(Environment environment) {
      * Returns the schema of the environment instance.
      * @return String
      */
-    protected String getEnvSchema() {
+    protected String schema() {
         if (environment == null) {
             throw new IllegalStateException("The environment has not been initialized. Cannot perform SQL queries " +
                                             "since we can't determine which database schema to operate on.");
@@ -50,7 +58,7 @@ protected String getEnvSchema() {
      * @return String
      */
     protected String table(String tableName) {
-        return getEnvSchema() + "." + tableName;
+        return schema() + "." + tableName;
     }
 
     /**
@@ -66,6 +74,25 @@ protected String orderBy(String columnName, SortOrder sortOrder) {
         return "ORDER BY " + columnName  + " " + sortOrder.name();
     }
 
+    /**
+     * Outputs the ORDER BY clause for multiple column name and sort order pairs.
+     * @param columnNames List<String>
+     * @param sortOrders List<SortOrder>>
+     * @return String
+     */
+    protected String orderBy(List<String> columnNames, List<SortOrder> sortOrders) {
+        if (columnNames.size() != sortOrders.size()) {
+            throw new IllegalArgumentException("For order by clause, number of column names must match sort orders!");
+        }
+        String orderByClause = "ORDER BY ";
+        for (int i = 0; i < columnNames.size(); i++) {
+            if (sortOrders.get(i) != null && !sortOrders.get(i).equals(SortOrder.NONE)) {
+                orderByClause += (columnNames.get(i) + " " + sortOrders.get(i).name() + ((i + 1 == columnNames.size()) ? " " : ", "));
+            }
+        }
+        return orderByClause;
+    }
+
     /** --- Static Helper Methods --- */
 
     /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fbase%2FSqlBaseDao.java
6f1f4eff6be8d09afb6d9e398e16255aeb473fe6
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fbill%2FSqlBillDao.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fbill%2FSqlBillDao.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
import gov.nysenate.openleg.model.bill.BillAction;
import gov.nysenate.openleg.model.bill.BillAmendment;
import gov.nysenate.openleg.model.bill.BillId;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
public class SqlBillDao extends SqlBaseDao implements BillDao
    /* --- Implemented Methods --- */

    @Override
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", printNo);
        params.addValue("sessionYear", sessionYear);
        try {
            Bill bill = jdbcNamed.queryForObject(SqlBillQuery.SELECT_BILL_SQL.getSql(schema()), params, new BillRowMapper());
            List<BillAmendment> billAmendments =
                jdbcNamed.query(SqlBillQuery.SELECT_BILL_AMENDMENTS_SQL.getSql(schema()), params, new BillAmendmentRowMapper());
            for (BillAmendment amendment : billAmendments) {
                params.addValue("version", amendment.getVersion());
                List<BillId> sameAsList =
                    jdbcNamed.query(SqlBillQuery.SELECT_BILL_SAME_AS_SQL.getSql(schema()), params, new BillSameAsRowMapper());
                amendment.setSameAs(new HashSet<>(sameAsList));
            }
            List<BillAction> billActions =
                jdbcNamed.query(SqlBillQuery.SELECT_BILL_ACTIONS_SQL.getSql(schema()), params, new BillActionRowMapper());
            bill.addAmendments(billAmendments);
            bill.setActions(billActions);
            return bill;
        }
        catch (EmptyResultDataAccessException ex) {
            logger.debug("Bill " + printNo + "-" + sessionYear + " does not exist in database.");
            return null;
        }
    }

    /**
     * {@inheritDoc}
     *
     * Updates information for an existing bill or creates new records if the bill is new.
     * Due to the normalized nature of the database it takes several queries to update all
     * the relevant pieces of data contained within the Bill object. The sobiFragment
     * reference is used to keep track of changes to the bill.
     */
    @Override
    public void updateBill(Bill bill, SOBIFragment sobiFragment) {
        // Update the bill record
        MapSqlParameterSource billParams = getBillParams(bill, sobiFragment);
        if (jdbcNamed.update(SqlBillQuery.UPDATE_BILL_SQL.getSql(schema()), billParams) == 0) {
            jdbcNamed.update(SqlBillQuery.INSERT_BILL_SQL.getSql(schema()), billParams);
        }
        // Update the bill amendments
        for (BillAmendment amendment : bill.getAmendmentList()) {
            MapSqlParameterSource amendParams = getBillAmendmentParams(amendment, sobiFragment);
            if (jdbcNamed.update(SqlBillQuery.UPDATE_BILL_AMENDMENT_SQL.getSql(schema()), amendParams) == 0) {
                jdbcNamed.update(SqlBillQuery.INSERT_BILL_AMENDMENT_SQL.getSql(schema()), amendParams);
            }
            // Update the same as bills
            List<BillId> existingSameAs =
                jdbcNamed.query(SqlBillQuery.SELECT_BILL_SAME_AS_SQL.getSql(schema()), amendParams, new BillSameAsRowMapper());
            if (existingSameAs.size() != amendment.getSameAs().size() || !existingSameAs.containsAll(amendment.getSameAs())) {
                jdbcNamed.update(SqlBillQuery.DELETE_SAME_AS_FOR_BILL_SQL.getSql(schema()), amendParams);
                for (BillId sameAsBillId : amendment.getSameAs()) {
                    MapSqlParameterSource sameAsParams = getBillSameAsParams(amendment, sameAsBillId, sobiFragment);
                    jdbcNamed.update(SqlBillQuery.INSERT_BILL_SAME_AS_SQL.getSql(schema()), sameAsParams);
                }
            }
        }
        // Determine which actions need to be inserted/deleted. Individual actions are not updated.
        List<BillAction> existingBillActions =
            jdbcNamed.query(SqlBillQuery.SELECT_BILL_ACTIONS_SQL.getSql(schema()), billParams, new BillActionRowMapper());
        List<BillAction> newBillActions = new ArrayList<>(bill.getActions());
        newBillActions.removeAll(existingBillActions);    // New actions to insert
        existingBillActions.removeAll(bill.getActions()); // Old actions to delete
        // Delete actions that are not in the updated list
        for (BillAction action : existingBillActions) {
            MapSqlParameterSource actionParams = getBillActionParams(action, sobiFragment);
            jdbcNamed.update(SqlBillQuery.DELETE_BILL_ACTION_SQL.getSql(schema()), actionParams);
        }
        // Insert all new actions
        for (BillAction action : newBillActions) {
            MapSqlParameterSource actionParams = getBillActionParams(action, sobiFragment);
            jdbcNamed.update(SqlBillQuery.INSERT_BILL_ACTION_SQL.getSql(schema()), actionParams);
        }
    /** {@inheritDoc} */
    @Override
    public void deleteBill(Bill bill) {
    }
    /** {@inheritDoc} */
    @Override
    public void publishBill(Bill bill) {
    }
    /** {@inheritDoc} */
    @Override
    public void unPublishBill(Bill bill) {

    }

    /** {@inheritDoc} */
    @Override
    public void deleteAllBills() {

    }

    /** --- Helper Classes --- */

    private class BillRowMapper implements RowMapper<Bill>
    {
        @Override
        public Bill mapRow(ResultSet rs, int rowNum) throws SQLException {
            Bill bill = new Bill();
            bill.setPrintNo(rs.getString("print_no"));
            bill.setSession(rs.getInt("session_year"));
            bill.setTitle(rs.getString("title"));
            bill.setLawSection(rs.getString("law_section"));
            bill.setLaw(rs.getString("law_code"));
            bill.setSummary(rs.getString("summary"));
            bill.setActiveVersion(rs.getString("active_version").trim());
            bill.setSponsor(null /** TODO */);
            bill.setYear(rs.getInt("active_year"));
            bill.setModifiedDate(rs.getDate("modified_date_time"));
            return bill;
        }
    }

    private class BillAmendmentRowMapper implements RowMapper<BillAmendment>
    {
        @Override
        public BillAmendment mapRow(ResultSet rs, int rowNum) throws SQLException {
            BillAmendment amend = new BillAmendment();
            amend.setBaseBillPrintNo(rs.getString("bill_print_no"));
            amend.setSession(rs.getInt("bill_session_year"));
            amend.setVersion(rs.getString("version"));
            amend.setMemo(rs.getString("sponsor_memo"));
            amend.setActClause(rs.getString("act_clause"));
            amend.setFulltext(rs.getString("full_text"));
            amend.setStricken(rs.getBoolean("stricken"));
            amend.setCurrentCommittee(null); /** TODO */
            amend.setUniBill(rs.getBoolean("uni_bill"));
            amend.setModifiedDate(rs.getDate("modified_date_time"));
            amend.setPublishDate(rs.getDate("published_date_time"));
            return amend;
        }
    }

    private class BillActionRowMapper implements RowMapper<BillAction>
    {
        @Override
        public BillAction mapRow(ResultSet rs, int rowNum) throws SQLException {
            BillAction billAction = new BillAction();
            billAction.setBaseBillPrintNo(rs.getString("bill_print_no"));
            billAction.setAmendmentVersion(rs.getString("bill_amend_version"));
            billAction.setSession(rs.getInt("bill_session_year"));
            billAction.setDate(rs.getDate("effect_date"));
            billAction.setText(rs.getString("text"));
            billAction.setModifiedDate(rs.getDate("modified_date_time"));
            billAction.setPublishDate(rs.getDate("published_date_time"));
            return billAction;
        }
    }

    private class BillSameAsRowMapper implements RowMapper<BillId>
    {
        @Override
        public BillId mapRow(ResultSet rs, int rowNum) throws SQLException {
            return new BillId(rs.getString("same_as_bill_print_no"), rs.getInt("same_as_session_year"),
                              rs.getString("same_as_amend_version"));

    /** --- Internal Methods --- */

    /**
     * Returns a MapSqlParameterSource with columns mapped to Bill values for use in update/insert queries on
     * the bill table.
     * @param bill String
     * @return MapSqlParameterSource
     */
    private MapSqlParameterSource getBillParams(Bill bill, SOBIFragment fragment) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", bill.getPrintNo());
        params.addValue("sessionYear", bill.getSession());
        params.addValue("title", bill.getTitle());
        params.addValue("lawSection", bill.getLawSection());
        params.addValue("lawCode", bill.getLaw());
        params.addValue("summary", bill.getSummary());
        params.addValue("activeVersion", bill.getActiveVersion());
        params.addValue("sponsorId", null /**TODO */);
        params.addValue("activeYear", bill.getYear());
        params.addValue("modifiedDateTime", toTimestamp(bill.getModifiedDate()));
        params.addValue("publishedDateTime", toTimestamp(bill.getPublishDate()));
        addSOBIFragmentParams(fragment, params);
        return params;
    }

    /**
     * Returns a MapSqlParameterSource with columns mapped to BillAmendment values for use in update/insert
     * queries on the bill amendment table.
     * @param amendment BillAmendment
     * @param fragment SOBIFragment
     * @return MapSqlParameterSource
     */
    private MapSqlParameterSource getBillAmendmentParams(BillAmendment amendment, SOBIFragment fragment) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", amendment.getBaseBillPrintNo());
        params.addValue("sessionYear", amendment.getSession());
        params.addValue("version", amendment.getVersion());
        params.addValue("sponsorMemo", amendment.getMemo());
        params.addValue("actClause", amendment.getActClause());
        params.addValue("fullText", amendment.getFulltext());
        params.addValue("stricken", amendment.isStricken());
        params.addValue("currentCommitteeId", null);
        params.addValue("uniBill", amendment.isUniBill());
        params.addValue("modifiedDateTime", toTimestamp(amendment.getModifiedDate()));
        params.addValue("publishedDateTime", toTimestamp(amendment.getPublishDate()));
        addSOBIFragmentParams(fragment, params);
        return params;
    }

    /**
     * Returns a MapSqlParameterSource with columns mapped to BillAction for use in inserting records
     * into the bill action table.
     * @param billAction BillAction
     * @param fragment SOBIFragment
     * @return MapSqlParameterSource
     */
    private MapSqlParameterSource getBillActionParams(BillAction billAction, SOBIFragment fragment) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", billAction.getBaseBillPrintNo());
        params.addValue("sessionYear", billAction.getSession());
        params.addValue("version", billAction.getAmendmentVersion());
        params.addValue("effectDate", billAction.getDate());
        params.addValue("text", billAction.getText());
        params.addValue("modifiedDateTime", toTimestamp(billAction.getModifiedDate()));
        params.addValue("publishedDateTime", toTimestamp(billAction.getPublishDate()));
        addSOBIFragmentParams(fragment, params);
        return params;
    }

    private MapSqlParameterSource getBillSameAsParams(BillAmendment billAmendment, BillId sameAs, SOBIFragment fragment) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", billAmendment.getBaseBillPrintNo());
        params.addValue("sessionYear", billAmendment.getSession());
        params.addValue("version", billAmendment.getVersion());
        params.addValue("sameAsPrintNo", sameAs.getBasePrintNo());
        params.addValue("sameAsSessionYear", sameAs.getSession());
        params.addValue("sameAsVersion", sameAs.getVersion());
        addSOBIFragmentParams(fragment, params);
        return params;
    }

    /**
     * Applies columns that identify a SOBIFragment to an existing MapSqlParameterSource.
     * @param fragment SOBIFragment
     * @param params MapSqlParameterSource
     */
    private void addSOBIFragmentParams(SOBIFragment fragment, MapSqlParameterSource params) {
        params.addValue("lastFragmentFileName", fragment.getFileName());
        params.addValue("lastFragmentType", fragment.getType().name());
    }
Bill bill  SOBIFragment sobiFragment
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
// Update the bill record
        MapSqlParameterSource billParams = getBillParams(bill, sobiFragment);
        if (jdbcNamed.update(SqlBillQuery.UPDATE_BILL_SQL.getSql(schema()), billParams) == 0) {
            jdbcNamed.update(SqlBillQuery.INSERT_BILL_SQL.getSql(schema()), billParams);
void updateBill(Bill bill, SOBIFragment sobiFragment)
BillAction action : existingBillActions
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
MapSqlParameterSource actionParams = getBillActionParams(action, sobiFragment);
            jdbcNamed.update(SqlBillQuery.DELETE_BILL_ACTION_SQL.getSql(schema()), actionParams);
list
        for (BillAction action : existingBillActions)
BillAction action : newBillActions
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
MapSqlParameterSource actionParams = getBillActionParams(action, sobiFragment);
            jdbcNamed.update(SqlBillQuery.INSERT_BILL_ACTION_SQL.getSql(schema()), actionParams);
actions
        for (BillAction action : newBillActions)
Bill bill
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
void deleteBill(Bill bill)
Bill bill
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
void publishBill(Bill bill)
Bill bill
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
void unPublishBill(Bill bill)
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
void deleteAllBills()
Bill bill  SOBIFragment fragment
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", bill.getPrintNo());
        params.addValue("sessionYear", bill.getSession());
        params.addValue("title", bill.getTitle());
        params.addValue("lawSection", bill.getLawSection());
        params.addValue("lawCode", bill.getLaw());
        params.addValue("summary", bill.getSummary());
        params.addValue("activeVersion", bill.getActiveVersion());
        params.addValue("sponsorId", null /**TODO */);
        params.addValue("activeYear", bill.getYear());
        params.addValue("modifiedDateTime", toTimestamp(bill.getModifiedDate()));
        params.addValue("publishedDateTime", toTimestamp(bill.getPublishDate()));
        addSOBIFragmentParams(fragment, params);
        return params;
MapSqlParameterSource getBillParams(Bill bill, SOBIFragment fragment)
BillAmendment amendment  SOBIFragment fragment
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", amendment.getBaseBillPrintNo());
        params.addValue("sessionYear", amendment.getSession());
        params.addValue("version", amendment.getVersion());
        params.addValue("sponsorMemo", amendment.getMemo());
        params.addValue("actClause", amendment.getActClause());
        params.addValue("fullText", amendment.getFulltext());
        params.addValue("stricken", amendment.isStricken());
        params.addValue("currentCommitteeId", null);
        params.addValue("uniBill", amendment.isUniBill());
        params.addValue("modifiedDateTime", toTimestamp(amendment.getModifiedDate()));
        params.addValue("publishedDateTime", toTimestamp(amendment.getPublishDate()));
        addSOBIFragmentParams(fragment, params);
        return params;
MapSqlParameterSource getBillAmendmentParams(BillAmendment amendment, SOBIFragment fragment)
BillAction billAction  SOBIFragment fragment
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", billAction.getBaseBillPrintNo());
        params.addValue("sessionYear", billAction.getSession());
        params.addValue("version", billAction.getAmendmentVersion());
        params.addValue("effectDate", billAction.getDate());
        params.addValue("text", billAction.getText());
        params.addValue("modifiedDateTime", toTimestamp(billAction.getModifiedDate()));
        params.addValue("publishedDateTime", toTimestamp(billAction.getPublishDate()));
        addSOBIFragmentParams(fragment, params);
        return params;
MapSqlParameterSource getBillActionParams(BillAction billAction, SOBIFragment fragment)
BillAmendment billAmendment  BillId sameAs  SOBIFragment fragment
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("printNo", billAmendment.getBaseBillPrintNo());
        params.addValue("sessionYear", billAmendment.getSession());
        params.addValue("version", billAmendment.getVersion());
        params.addValue("sameAsPrintNo", sameAs.getBasePrintNo());
        params.addValue("sameAsSessionYear", sameAs.getSession());
        params.addValue("sameAsVersion", sameAs.getVersion());
        addSOBIFragmentParams(fragment, params);
        return params;
MapSqlParameterSource getBillSameAsParams(BillAmendment billAmendment, BillId sameAs, SOBIFragment fragment)
SOBIFragment fragment  MapSqlParameterSource params
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
params.addValue("lastFragmentFileName", fragment.getFileName());
        params.addValue("lastFragmentType", fragment.getType().name());
void addSOBIFragmentParams(SOBIFragment fragment, MapSqlParameterSource params)
import java.sql.Timestamp;
public class SqlBillDao extends SqlBaseDao
    public SqlBillDao() {
        super(null);
    }

        return null;
    private static String UPDATE_BILL_SQL =
        "UPDATE " + DEFAULT_ENV_SCHEMA + ".bill \n" +
        "SET title = ?, law_section = ?, summary = ?, active_version = ?, sponsor_id = ?, active_year = ?, " +
        "    modified_date_time = ?, published_date_time = ? \n" +
        "WHERE print_no = ? AND session_year = ?";
    private static String INSERT_BILL_SQL =
        "INSERT INTO " + DEFAULT_ENV_SCHEMA + ".bill (print_no, session_year, title, law_section, summary, active_version, " +
        "                                    sponsor_id, active_year, modified_date_time, published_date_time)\n" +
        "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ? \n" +
        "WHERE NOT EXISTS (SELECT 1 FROM " + DEFAULT_ENV_SCHEMA + ".bill WHERE print_no = ? AND session_year = ?)";
    public void saveBill(Bill bill, SOBIFragment sobiFragment) {
        if (bill != null) {
            /* try {
                runner.update(UPDATE_BILL_SQL, bill.getTitle(), bill.getLawSection(), bill.getSummary(), bill.getActiveVersion(),
                        bill.getSponsor().getId(), bill.getYear(), new Timestamp(bill.getModifiedDate().getTime()),
                        new Timestamp(bill.getPublishDate().getTime()), bill.getPrintNumber(), bill.getSession());
                runner.update(INSERT_BILL_SQL,
                        bill.getPrintNumber(), bill.getSession(), bill.getTitle(), bill.getLawSection(),
                        bill.getSummary(), bill.getActiveVersion(), bill.getSponsor().getId(), bill.getYear(),
                        new Timestamp(bill.getModifiedDate().getTime()), new Timestamp(bill.getPublishDate().getTime()), bill.getPrintNumber(), bill.getSession());
            }
            catch (SQLException e) {
                logger.error("Failed to ", e);
            } */
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
super(null);
public SqlBillDao()
Bill bill  SOBIFragment sobiFragment
src/main/java/gov/nysenate/openleg/dao/bill/SqlBillDao.java
if (bill != null) {
            /* try {
                runner.update(UPDATE_BILL_SQL, bill.getTitle(), bill.getLawSection(), bill.getSummary(), bill.getActiveVersion(),
                        bill.getSponsor().getId(), bill.getYear(), new Timestamp(bill.getModifiedDate().getTime()),
                        new Timestamp(bill.getPublishDate().getTime()), bill.getPrintNumber(), bill.getSession());
                runner.update(INSERT_BILL_SQL,
                        bill.getPrintNumber(), bill.getSession(), bill.getTitle(), bill.getLawSection(),
                        bill.getSummary(), bill.getActiveVersion(), bill.getSponsor().getId(), bill.getYear(),
                        new Timestamp(bill.getModifiedDate().getTime()), new Timestamp(bill.getPublishDate().getTime()), bill.getPrintNumber(), bill.getSession());
void saveBill(Bill bill, SOBIFragment sobiFragment)
@@ -3,56 +3,287 @@
 import gov.nysenate.openleg.Environment;
 import gov.nysenate.openleg.dao.base.SqlBaseDao;
 import gov.nysenate.openleg.model.bill.Bill;
+import gov.nysenate.openleg.model.bill.BillAction;
+import gov.nysenate.openleg.model.bill.BillAmendment;
+import gov.nysenate.openleg.model.bill.BillId;
 import gov.nysenate.openleg.model.sobi.SOBIFragment;
 import org.apache.log4j.Logger;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
 
+import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
 
-public class SqlBillDao extends SqlBaseDao
+public class SqlBillDao extends SqlBaseDao implements BillDao
 {
     private static Logger logger = Logger.getLogger(SqlBillDao.class);
 
-    public SqlBillDao() {
-        super(null);
-    }
-
     public SqlBillDao(Environment environment) {
         super(environment);
     }
 
+    /* --- Implemented Methods --- */
+
+    @Override
     public Bill getBill(String printNo, int sessionYear) {
-        return null;
+        MapSqlParameterSource params = new MapSqlParameterSource();
+        params.addValue("printNo", printNo);
+        params.addValue("sessionYear", sessionYear);
+        try {
+            Bill bill = jdbcNamed.queryForObject(SqlBillQuery.SELECT_BILL_SQL.getSql(schema()), params, new BillRowMapper());
+            List<BillAmendment> billAmendments =
+                jdbcNamed.query(SqlBillQuery.SELECT_BILL_AMENDMENTS_SQL.getSql(schema()), params, new BillAmendmentRowMapper());
+            for (BillAmendment amendment : billAmendments) {
+                params.addValue("version", amendment.getVersion());
+                List<BillId> sameAsList =
+                    jdbcNamed.query(SqlBillQuery.SELECT_BILL_SAME_AS_SQL.getSql(schema()), params, new BillSameAsRowMapper());
+                amendment.setSameAs(new HashSet<>(sameAsList));
+            }
+            List<BillAction> billActions =
+                jdbcNamed.query(SqlBillQuery.SELECT_BILL_ACTIONS_SQL.getSql(schema()), params, new BillActionRowMapper());
+            bill.addAmendments(billAmendments);
+            bill.setActions(billActions);
+            return bill;
+        }
+        catch (EmptyResultDataAccessException ex) {
+            logger.debug("Bill " + printNo + "-" + sessionYear + " does not exist in database.");
+            return null;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Updates information for an existing bill or creates new records if the bill is new.
+     * Due to the normalized nature of the database it takes several queries to update all
+     * the relevant pieces of data contained within the Bill object. The sobiFragment
+     * reference is used to keep track of changes to the bill.
+     */
+    @Override
+    public void updateBill(Bill bill, SOBIFragment sobiFragment) {
+        // Update the bill record
+        MapSqlParameterSource billParams = getBillParams(bill, sobiFragment);
+        if (jdbcNamed.update(SqlBillQuery.UPDATE_BILL_SQL.getSql(schema()), billParams) == 0) {
+            jdbcNamed.update(SqlBillQuery.INSERT_BILL_SQL.getSql(schema()), billParams);
+        }
+        // Update the bill amendments
+        for (BillAmendment amendment : bill.getAmendmentList()) {
+            MapSqlParameterSource amendParams = getBillAmendmentParams(amendment, sobiFragment);
+            if (jdbcNamed.update(SqlBillQuery.UPDATE_BILL_AMENDMENT_SQL.getSql(schema()), amendParams) == 0) {
+                jdbcNamed.update(SqlBillQuery.INSERT_BILL_AMENDMENT_SQL.getSql(schema()), amendParams);
+            }
+            // Update the same as bills
+            List<BillId> existingSameAs =
+                jdbcNamed.query(SqlBillQuery.SELECT_BILL_SAME_AS_SQL.getSql(schema()), amendParams, new BillSameAsRowMapper());
+            if (existingSameAs.size() != amendment.getSameAs().size() || !existingSameAs.containsAll(amendment.getSameAs())) {
+                jdbcNamed.update(SqlBillQuery.DELETE_SAME_AS_FOR_BILL_SQL.getSql(schema()), amendParams);
+                for (BillId sameAsBillId : amendment.getSameAs()) {
+                    MapSqlParameterSource sameAsParams = getBillSameAsParams(amendment, sameAsBillId, sobiFragment);
+                    jdbcNamed.update(SqlBillQuery.INSERT_BILL_SAME_AS_SQL.getSql(schema()), sameAsParams);
+                }
+            }
+        }
+        // Determine which actions need to be inserted/deleted. Individual actions are not updated.
+        List<BillAction> existingBillActions =
+            jdbcNamed.query(SqlBillQuery.SELECT_BILL_ACTIONS_SQL.getSql(schema()), billParams, new BillActionRowMapper());
+        List<BillAction> newBillActions = new ArrayList<>(bill.getActions());
+        newBillActions.removeAll(existingBillActions);    // New actions to insert
+        existingBillActions.removeAll(bill.getActions()); // Old actions to delete
+        // Delete actions that are not in the updated list
+        for (BillAction action : existingBillActions) {
+            MapSqlParameterSource actionParams = getBillActionParams(action, sobiFragment);
+            jdbcNamed.update(SqlBillQuery.DELETE_BILL_ACTION_SQL.getSql(schema()), actionParams);
+        }
+        // Insert all new actions
+        for (BillAction action : newBillActions) {
+            MapSqlParameterSource actionParams = getBillActionParams(action, sobiFragment);
+            jdbcNamed.update(SqlBillQuery.INSERT_BILL_ACTION_SQL.getSql(schema()), actionParams);
+        }
     }
 
-    private static String UPDATE_BILL_SQL =
-        "UPDATE " + DEFAULT_ENV_SCHEMA + ".bill \n" +
-        "SET title = ?, law_section = ?, summary = ?, active_version = ?, sponsor_id = ?, active_year = ?, " +
-        "    modified_date_time = ?, published_date_time = ? \n" +
-        "WHERE print_no = ? AND session_year = ?";
+    /** {@inheritDoc} */
+    @Override
+    public void deleteBill(Bill bill) {
 
-    private static String INSERT_BILL_SQL =
-        "INSERT INTO " + DEFAULT_ENV_SCHEMA + ".bill (print_no, session_year, title, law_section, summary, active_version, " +
-        "                                    sponsor_id, active_year, modified_date_time, published_date_time)\n" +
-        "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ? \n" +
-        "WHERE NOT EXISTS (SELECT 1 FROM " + DEFAULT_ENV_SCHEMA + ".bill WHERE print_no = ? AND session_year = ?)";
+    }
 
-    public void saveBill(Bill bill, SOBIFragment sobiFragment) {
-        if (bill != null) {
-            /* try {
+    /** {@inheritDoc} */
+    @Override
+    public void publishBill(Bill bill) {
 
-                runner.update(UPDATE_BILL_SQL, bill.getTitle(), bill.getLawSection(), bill.getSummary(), bill.getActiveVersion(),
-                        bill.getSponsor().getId(), bill.getYear(), new Timestamp(bill.getModifiedDate().getTime()),
-                        new Timestamp(bill.getPublishDate().getTime()), bill.getPrintNumber(), bill.getSession());
+    }
 
-                runner.update(INSERT_BILL_SQL,
-                        bill.getPrintNumber(), bill.getSession(), bill.getTitle(), bill.getLawSection(),
-                        bill.getSummary(), bill.getActiveVersion(), bill.getSponsor().getId(), bill.getYear(),
-                        new Timestamp(bill.getModifiedDate().getTime()), new Timestamp(bill.getPublishDate().getTime()), bill.getPrintNumber(), bill.getSession());
-            }
-            catch (SQLException e) {
-                logger.error("Failed to ", e);
-            } */
+    /** {@inheritDoc} */
+    @Override
+    public void unPublishBill(Bill bill) {
+
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void deleteAllBills() {
+
+    }
+
+    /** --- Helper Classes --- */
+
+    private class BillRowMapper implements RowMapper<Bill>
+    {
+        @Override
+        public Bill mapRow(ResultSet rs, int rowNum) throws SQLException {
+            Bill bill = new Bill();
+            bill.setPrintNo(rs.getString("print_no"));
+            bill.setSession(rs.getInt("session_year"));
+            bill.setTitle(rs.getString("title"));
+            bill.setLawSection(rs.getString("law_section"));
+            bill.setLaw(rs.getString("law_code"));
+            bill.setSummary(rs.getString("summary"));
+            bill.setActiveVersion(rs.getString("active_version").trim());
+            bill.setSponsor(null /** TODO */);
+            bill.setYear(rs.getInt("active_year"));
+            bill.setModifiedDate(rs.getDate("modified_date_time"));
+            return bill;
+        }
+    }
+
+    private class BillAmendmentRowMapper implements RowMapper<BillAmendment>
+    {
+        @Override
+        public BillAmendment mapRow(ResultSet rs, int rowNum) throws SQLException {
+            BillAmendment amend = new BillAmendment();
+            amend.setBaseBillPrintNo(rs.getString("bill_print_no"));
+            amend.setSession(rs.getInt("bill_session_year"));
+            amend.setVersion(rs.getString("version"));
+            amend.setMemo(rs.getString("sponsor_memo"));
+            amend.setActClause(rs.getString("act_clause"));
+            amend.setFulltext(rs.getString("full_text"));
+            amend.setStricken(rs.getBoolean("stricken"));
+            amend.setCurrentCommittee(null); /** TODO */
+            amend.setUniBill(rs.getBoolean("uni_bill"));
+            amend.setModifiedDate(rs.getDate("modified_date_time"));
+            amend.setPublishDate(rs.getDate("published_date_time"));
+            return amend;
+        }
+    }
+
+    private class BillActionRowMapper implements RowMapper<BillAction>
+    {
+        @Override
+        public BillAction mapRow(ResultSet rs, int rowNum) throws SQLException {
+            BillAction billAction = new BillAction();
+            billAction.setBaseBillPrintNo(rs.getString("bill_print_no"));
+            billAction.setAmendmentVersion(rs.getString("bill_amend_version"));
+            billAction.setSession(rs.getInt("bill_session_year"));
+            billAction.setDate(rs.getDate("effect_date"));
+            billAction.setText(rs.getString("text"));
+            billAction.setModifiedDate(rs.getDate("modified_date_time"));
+            billAction.setPublishDate(rs.getDate("published_date_time"));
+            return billAction;
+        }
+    }
+
+    private class BillSameAsRowMapper implements RowMapper<BillId>
+    {
+        @Override
+        public BillId mapRow(ResultSet rs, int rowNum) throws SQLException {
+            return new BillId(rs.getString("same_as_bill_print_no"), rs.getInt("same_as_session_year"),
+                              rs.getString("same_as_amend_version"));
         }
     }
+
+    /** --- Internal Methods --- */
+
+    /**
+     * Returns a MapSqlParameterSource with columns mapped to Bill values for use in update/insert queries on
+     * the bill table.
+     * @param bill String
+     * @return MapSqlParameterSource
+     */
+    private MapSqlParameterSource getBillParams(Bill bill, SOBIFragment fragment) {
+        MapSqlParameterSource params = new MapSqlParameterSource();
+        params.addValue("printNo", bill.getPrintNo());
+        params.addValue("sessionYear", bill.getSession());
+        params.addValue("title", bill.getTitle());
+        params.addValue("lawSection", bill.getLawSection());
+        params.addValue("lawCode", bill.getLaw());
+        params.addValue("summary", bill.getSummary());
+        params.addValue("activeVersion", bill.getActiveVersion());
+        params.addValue("sponsorId", null /**TODO */);
+        params.addValue("activeYear", bill.getYear());
+        params.addValue("modifiedDateTime", toTimestamp(bill.getModifiedDate()));
+        params.addValue("publishedDateTime", toTimestamp(bill.getPublishDate()));
+        addSOBIFragmentParams(fragment, params);
+        return params;
+    }
+
+    /**
+     * Returns a MapSqlParameterSource with columns mapped to BillAmendment values for use in update/insert
+     * queries on the bill amendment table.
+     * @param amendment BillAmendment
+     * @param fragment SOBIFragment
+     * @return MapSqlParameterSource
+     */
+    private MapSqlParameterSource getBillAmendmentParams(BillAmendment amendment, SOBIFragment fragment) {
+        MapSqlParameterSource params = new MapSqlParameterSource();
+        params.addValue("printNo", amendment.getBaseBillPrintNo());
+        params.addValue("sessionYear", amendment.getSession());
+        params.addValue("version", amendment.getVersion());
+        params.addValue("sponsorMemo", amendment.getMemo());
+        params.addValue("actClause", amendment.getActClause());
+        params.addValue("fullText", amendment.getFulltext());
+        params.addValue("stricken", amendment.isStricken());
+        params.addValue("currentCommitteeId", null);
+        params.addValue("uniBill", amendment.isUniBill());
+        params.addValue("modifiedDateTime", toTimestamp(amendment.getModifiedDate()));
+        params.addValue("publishedDateTime", toTimestamp(amendment.getPublishDate()));
+        addSOBIFragmentParams(fragment, params);
+        return params;
+    }
+
+    /**
+     * Returns a MapSqlParameterSource with columns mapped to BillAction for use in inserting records
+     * into the bill action table.
+     * @param billAction BillAction
+     * @param fragment SOBIFragment
+     * @return MapSqlParameterSource
+     */
+    private MapSqlParameterSource getBillActionParams(BillAction billAction, SOBIFragment fragment) {
+        MapSqlParameterSource params = new MapSqlParameterSource();
+        params.addValue("printNo", billAction.getBaseBillPrintNo());
+        params.addValue("sessionYear", billAction.getSession());
+        params.addValue("version", billAction.getAmendmentVersion());
+        params.addValue("effectDate", billAction.getDate());
+        params.addValue("text", billAction.getText());
+        params.addValue("modifiedDateTime", toTimestamp(billAction.getModifiedDate()));
+        params.addValue("publishedDateTime", toTimestamp(billAction.getPublishDate()));
+        addSOBIFragmentParams(fragment, params);
+        return params;
+    }
+
+    private MapSqlParameterSource getBillSameAsParams(BillAmendment billAmendment, BillId sameAs, SOBIFragment fragment) {
+        MapSqlParameterSource params = new MapSqlParameterSource();
+        params.addValue("printNo", billAmendment.getBaseBillPrintNo());
+        params.addValue("sessionYear", billAmendment.getSession());
+        params.addValue("version", billAmendment.getVersion());
+        params.addValue("sameAsPrintNo", sameAs.getBasePrintNo());
+        params.addValue("sameAsSessionYear", sameAs.getSession());
+        params.addValue("sameAsVersion", sameAs.getVersion());
+        addSOBIFragmentParams(fragment, params);
+        return params;
+    }
+
+    /**
+     * Applies columns that identify a SOBIFragment to an existing MapSqlParameterSource.
+     * @param fragment SOBIFragment
+     * @param params MapSqlParameterSource
+     */
+    private void addSOBIFragmentParams(SOBIFragment fragment, MapSqlParameterSource params) {
+        params.addValue("lastFragmentFileName", fragment.getFileName());
+        params.addValue("lastFragmentType", fragment.getType().name());
+    }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fbill%2FSqlBillDao.java
b8e7d514ba9e18cbf9b7b2b63ac9ba1764c6bdec
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSOBIFileDao.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSOBIFileDao.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/dao/sobi/SOBIFileDao.java
 *
 * A typical call stack when processing SOBI files could look like the following:
 * 1. stageSOBIFiles(true)                - Stage new files for processing
 * 2. getPendingSOBIFiles(SortOrder.ASC)  - Retrieve those new files
 * 3. [ Do some processing ]              - Create and process SOBIFragments somewhere
 * 4. updateSOBIFile(..)                  - Update record after processing for the file is done
     * If the record doesn't already exist, no action will be taken.
@@ -10,6 +10,12 @@
 
 /**
  * DAO interface for managing and persisting SOBIFiles.
+ *
+ * A typical call stack when processing SOBI files could look like the following:
+ * 1. stageSOBIFiles(true)                - Stage new files for processing
+ * 2. getPendingSOBIFiles(SortOrder.ASC)  - Retrieve those new files
+ * 3. [ Do some processing ]              - Create and process SOBIFragments somewhere
+ * 4. updateSOBIFile(..)                  - Update record after processing for the file is done
  */
 public interface SOBIFileDao
 {
@@ -70,6 +76,7 @@ public interface SOBIFileDao
 
     /**
      * Updates an existing SOBIFile in the backing store with the given instance.
+     * If the record doesn't already exist, no action will be taken.
      *
      * @param sobiFile SOBIFile
      * @return boolean - true if record was updated, false if nothing was updated
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSOBIFileDao.java
b02b83ac12df33822545ae9365c621ef53f31afc
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSOBIFragmentDao.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSOBIFragmentDao.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/dao/sobi/SOBIFragmentDao.java
import gov.nysenate.openleg.dao.base.SortOrder;
import gov.nysenate.openleg.model.sobi.SOBIFile;
import gov.nysenate.openleg.model.sobi.SOBIFragmentType;
import java.util.List;

/**
 * DAO interface for managing and persisting SOBIFragments.
 */
    /** --- Retrieval Methods --- */

    public SOBIFragment getSOBIFragment(SOBIFragmentType fragmentType, String fragmentFileName);

    public List<SOBIFragment> getSOBIFragments(SOBIFile sobiFile, SortOrder order);

    public List<SOBIFragment> getSOBIFragments(SOBIFile sobiFile, SOBIFragmentType fragmentType, SortOrder order);

    /** --- Processing/Insertion Methods --- */

     * Persist the sobi fragments into the backing store. It is assumed that the SOBIFile objects have already
     * been staged otherwise the fragment cannot be recorded.
     * @param fragments List<SOBIFragment>
    public void saveSOBIFragments(List<SOBIFragment> fragments);
     * Delete the fragments that were generated for the given sobiFile.
     * @param sobiFile SOBIFile
    public void deleteSOBIFragments(SOBIFile sobiFile);
     *
     * @param fragmentFileName
     * @return
    public SOBIFragment getSOBIFragmentByFileName(String fragmentFileName);
     *
     * @param fragment
    public void saveSOBIFragment(SOBIFragment fragment);
@@ -1,19 +1,37 @@
 package gov.nysenate.openleg.dao.sobi;
 
+import gov.nysenate.openleg.dao.base.SortOrder;
+import gov.nysenate.openleg.model.sobi.SOBIFile;
 import gov.nysenate.openleg.model.sobi.SOBIFragment;
+import gov.nysenate.openleg.model.sobi.SOBIFragmentType;
 
+import java.util.List;
+
+/**
+ * DAO interface for managing and persisting SOBIFragments.
+ */
 public interface SOBIFragmentDao
 {
+    /** --- Retrieval Methods --- */
+
+    public SOBIFragment getSOBIFragment(SOBIFragmentType fragmentType, String fragmentFileName);
+
+    public List<SOBIFragment> getSOBIFragments(SOBIFile sobiFile, SortOrder order);
+
+    public List<SOBIFragment> getSOBIFragments(SOBIFile sobiFile, SOBIFragmentType fragmentType, SortOrder order);
+
+    /** --- Processing/Insertion Methods --- */
+
     /**
-     *
-     * @param fragmentFileName
-     * @return
+     * Persist the sobi fragments into the backing store. It is assumed that the SOBIFile objects have already
+     * been staged otherwise the fragment cannot be recorded.
+     * @param fragments List<SOBIFragment>
      */
-    public SOBIFragment getSOBIFragmentByFileName(String fragmentFileName);
+    public void saveSOBIFragments(List<SOBIFragment> fragments);
 
     /**
-     *
-     * @param fragment
+     * Delete the fragments that were generated for the given sobiFile.
+     * @param sobiFile SOBIFile
      */
-    public void saveSOBIFragment(SOBIFragment fragment);
+    public void deleteSOBIFragments(SOBIFile sobiFile);
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSOBIFragmentDao.java
5b8b3b6d015211128e7fa11a10164fa92ad96170
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSqlSOBIFileDao.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSqlSOBIFileDao.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFileDao.java
import org.springframework.dao.DuplicateKeyException;
import java.io.FileNotFoundException;
        "INSERT INTO " + table(SOBI_FILE_TABLE) + "\n" +
        "(file_name, published_date_time, staged_date_time, processed_date_time, processed_count, pending_processing) " +
        "VALUES (:fileName, :publishedDateTime, :stagedDateTime, :processedDateTime, :processedCount, :pendingProcessing)";
        "    staged_date_time = :stagedDateTime," +
        return jdbcNamed.queryForObject(GET_SOBI_FILES_BY_FILE_NAMES_SQL, params, new SOBIFileRowMapper());
        List<SOBIFile> sobiList = jdbcNamed.query(GET_SOBI_FILES_BY_FILE_NAMES_SQL, params, new SOBIFileRowMapper());
        for (SOBIFile sobiFile : sobiList) {
            sobiFileMap.put(sobiFile.getFileName(), sobiFile);
        return jdbcNamed.query(GET_SOBI_FILES_DURING_SQL + orderBy("published_date_time", sortByPubDate),
                params, new SOBIFileRowMapper());
        return jdbc.query(GET_PENDING_SOBI_FILES_SQL + orderBy("published_date_time", sortByPubDate),                new SOBIFileRowMapper());
            sobiFile.setStagedDateTime(new Date());
                moveFileToWorking(file);
            catch (DuplicateKeyException ex) {
                        existingSOBIFile.setStagedDateTime(new Date());
                        existingSOBIFile.setPendingProcessing(true);
                        updateSOBIFile(existingSOBIFile);
                        moveFileToWorking(file);
                        filesStaged++;
                        logger.debug("Re-staged " + existingSOBIFile + " with latest file contents.");
                    logger.debug("Ignoring " + sobiFile + " since re-staging is disabled and file was previously processed.");
                int affected = updateSOBIMetadataInDatabase(sobiFile);
                    if (!sobiFile.isPendingProcessing()) {
                        File workFile = getFileInWorkingDirectory(sobiFile.getFileName());
                        if (workFile.exists()) {
                            moveFiletoArchive(workFile, sobiFile.getPublishedDateTime());
                        }
                    }
    /**
     * Maps rows from the sobi table to SOBIFile objects.
     */
    protected class SOBIFileRowMapper implements RowMapper<SOBIFile> {
        private String pfx = "";

        public SOBIFileRowMapper(){}
        public SOBIFileRowMapper(String pfx) {
            this.pfx = pfx;
        @Override
        public SOBIFile mapRow(ResultSet rs, int rowNum) throws SQLException {
            String fileName = rs.getString(pfx + "file_name");
            Date publishedDateTime = rs.getDate(pfx + "published_date_time");
            boolean pendingProcessing = rs.getBoolean(pfx + "pending_processing");
            File file = getSOBIFileFromFileSystem(fileName, publishedDateTime, pendingProcessing);
            try {
                SOBIFile sobiFile = new SOBIFile(file);
                sobiFile.setPendingProcessing(pendingProcessing);
                sobiFile.setProcessedDateTime(rs.getDate(pfx + "processed_date_time"));
                sobiFile.setProcessedCount(rs.getInt(pfx + "processed_count"));
                return sobiFile;
            catch (FileNotFoundException ex) {
                logger.error("SOBI file " + rs.getString("file_name") + " was not found in the expected location! \n" +
                        "This could be a result of modifications to the sobi file directory that were not synced with " +
                        "the database. ", ex);
            }
            catch (IOException ex) {
                logger.error(ex);
            }
            return null;
    /** --- Internal Methods --- */

        jdbcNamed.update(INSERT_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
    }

    /**
     * Updates the metadata of the SOBIFile into the database.
     * @throws java.lang.IllegalArgumentException if sobiFile is null
     */
    private int updateSOBIMetadataInDatabase(SOBIFile sobiFile) throws DataAccessException {
        return jdbcNamed.update(UPDATE_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
    private File getSOBIFileFromFileSystem(String fileName, Date publishedDateTime, boolean pendingProcessing) {
        return (pendingProcessing) ? getFileInWorkingDirectory(fileName) : getFileInArchiveDirectory(fileName, publishedDateTime);
     * Moves the file into the working SOBI file directory.
     * @param file File
    private void moveFileToWorking(File file) throws IOException {
        File workingFile = getFileInWorkingDirectory(file.getName());
        if (workingFile.exists()) {
            FileUtils.deleteQuietly(workingFile);
        FileUtils.moveFile(file, workingFile);
    }

    /**
     * Moves the file into the archive SOBI file directory.
     * @param file File
     */
    private void moveFiletoArchive(File file, Date publishedDateTime) throws IOException {
        File archiveFile = getFileInArchiveDirectory(file.getName(), publishedDateTime);
        if (archiveFile.exists()) {
            FileUtils.deleteQuietly(archiveFile);
        FileUtils.moveFile(file, archiveFile);
    }

    /**
     * Delete any traces of the SOBI file (either in work or archive directory) from the file system.
     * @param fileName String
     * @param publishedDateTime Date
     */
    private void deleteSOBIFileFromFileSystem(String fileName, Date publishedDateTime) {
        FileUtils.deleteQuietly(getFileInWorkingDirectory(fileName));
        FileUtils.deleteQuietly(getFileInArchiveDirectory(fileName, publishedDateTime));
     * @param publishedDateTime Date
    private File getFileInArchiveDirectory(String fileName, Date publishedDateTime) {
        String year = Integer.toString(new LocalDate(publishedDateTime).getYear());
     * Returns a MapSqlParameterSource with columns mapped to SOBIFile values for use in update/insert queries.
        params.addValue("stagedDateTime", toTimestamp(sobiFile.getStagedDateTime()));
}
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFileDao.java
public SOBIFileRowMapper()
String pfx
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFileDao.java
this.pfx = pfx;
        @Override
        public SOBIFile mapRow(ResultSet rs, int rowNum) throws SQLException {
            String fileName = rs.getString(pfx + "file_name");
            Date publishedDateTime = rs.getDate(pfx + "published_date_time");
            boolean pendingProcessing = rs.getBoolean(pfx + "pending_processing");
            File file = getSOBIFileFromFileSystem(fileName, publishedDateTime, pendingProcessing);
            try {
                SOBIFile sobiFile = new SOBIFile(file);
                sobiFile.setPendingProcessing(pendingProcessing);
                sobiFile.setProcessedDateTime(rs.getDate(pfx + "processed_date_time"));
                sobiFile.setProcessedCount(rs.getInt(pfx + "processed_count"));
                return sobiFile;
            catch (FileNotFoundException ex) {
                logger.error("SOBI file " + rs.getString("file_name") + " was not found in the expected location! \n" +
                        "This could be a result of modifications to the sobi file directory that were not synced with " +
                        "the database. ", ex);
public SOBIFileRowMapper(String pfx)
String fileName  Date publishedDateTime  boolean pendingProcessing
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFileDao.java
return (pendingProcessing) ? getFileInWorkingDirectory(fileName) : getFileInArchiveDirectory(fileName, publishedDateTime);
     * Moves the file into the working SOBI file directory.
     * @param file File
    private void moveFileToWorking(File file) throws IOException {
        File workingFile = getFileInWorkingDirectory(file.getName());
        if (workingFile.exists()) {
            FileUtils.deleteQuietly(workingFile);
        FileUtils.moveFile(file, workingFile);
File getSOBIFileFromFileSystem(String fileName, Date publishedDateTime, boolean pendingProcessing)
String fileName  Date publishedDateTime
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFileDao.java
FileUtils.deleteQuietly(getFileInWorkingDirectory(fileName));
        FileUtils.deleteQuietly(getFileInArchiveDirectory(fileName, publishedDateTime));
     * @param publishedDateTime Date
    private File getFileInArchiveDirectory(String fileName, Date publishedDateTime) {
        String year = Integer.toString(new LocalDate(publishedDateTime).getYear());
     * Returns a MapSqlParameterSource with columns mapped to SOBIFile values for use in update/insert queries.
        params.addValue("stagedDateTime", toTimestamp(sobiFile.getStagedDateTime()));
void deleteSOBIFileFromFileSystem(String fileName, Date publishedDateTime)
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import static gov.nysenate.openleg.util.FileHelper.moveFileToDirectory;
        "INSERT INTO " + table(SOBI_FILE_TABLE) + " (file_name, published_date_time, processed_date_time, processed_count, pending_processing) " +
        "VALUES (:fileName, :publishedDateTime, :processedDateTime, :processedCount, :pendingProcessing)";
        try {
            SOBIMetadata metaData = jdbcNamed.queryForObject(GET_SOBI_FILES_BY_FILE_NAMES_SQL, params, new SOBIMetadata.Mapper());
            sobiFile = new SOBIFile(getSOBIFileFromFileSystem(metaData));
            metaData.applyToSOBIFile(sobiFile);
        }
        catch (IncorrectResultSizeDataAccessException ex) {
            logger.debug("No sobi details were found in database for " + fileName, ex);
        }
        catch (IOException ex) {
            logger.error("Possible inconsistency between file system and database! " +
                         "Failed to construct SOBIFile instance with file name " + fileName, ex);
        }
        return sobiFile;
        List<SOBIMetadata> metaDataList = jdbcNamed.query(GET_SOBI_FILES_BY_FILE_NAMES_SQL, params, new SOBIMetadata.Mapper());
        try {
            for (SOBIMetadata metaData : metaDataList) {
                SOBIFile sobiFile = new SOBIFile(getSOBIFileFromFileSystem(metaData));
                metaData.applyToSOBIFile(sobiFile);
                sobiFileMap.put(sobiFile.getFileName(), sobiFile);
            }
        }
        catch (IOException ex) {
            logger.error("Failed to construct SOBIFile instance!", ex);
            return null;
        List<SOBIMetadata> metaDataList = jdbcNamed.query(GET_SOBI_FILES_DURING_SQL + orderBy("published_date_time", sortByPubDate),
                                                          params, new SOBIMetadata.Mapper());
        return getSobiFileListFromMetaDataList(metaDataList);
        List<SOBIMetadata> metaDataList =
            jdbc.query(GET_PENDING_SOBI_FILES_SQL + orderBy("published_date_time", sortByPubDate), new SOBIMetadata.Mapper());
        return getSobiFileListFromMetaDataList(metaDataList);

                if (!file.delete()) {
                    logger.fatal("Failed to delete " + file.getAbsoluteFile() + " from the sobi staging directory!");
                }
                updateSOBIFileInFileSystem(sobiFile);
            catch(DataIntegrityViolationException ex) {
                        File existingFile = getSOBIFileFromFileSystem(existingSOBIFile);
                        if (existingFile.exists()) {
                            moveFileToDirectory(file, this.workingSobiDir, true);
                            existingSOBIFile.setPendingProcessing(true);
                            updateSOBIFile(existingSOBIFile);
                            filesStaged++;
                            logger.debug("Re-staged " + existingSOBIFile + " with latest file contents.");
                        }
                        else {
                            logger.warn("Attempted to re-stage " + existingSOBIFile + " but the file " +
                                    existingFile.getAbsolutePath() + " doesn't exist!");
                        }
                    logger.debug("Skipping " + sobiFile + " since re-staging is disabled.");
                int affected = jdbcNamed.update(UPDATE_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
                    updateSOBIFileInFileSystem(sobiFile);
    /** Models the processing details for a sobi file which is stored in the database */
    protected static class SOBIMetadata {
        String fileName;
        Date publishedDateTime;
        Date processedDateTime;
        boolean pendingProcessing;
        int processedCount;

        /** Apply metadata info to SOBIFile */
        void applyToSOBIFile(SOBIFile sobiFile) {
            sobiFile.setProcessedDateTime(this.processedDateTime);
            sobiFile.setPendingProcessing(this.pendingProcessing);
            sobiFile.setProcessedCount(this.processedCount);
        /** Row Mapper implementation */
        static class Mapper implements RowMapper<SOBIMetadata> {
            @Override
            public SOBIMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {
                SOBIMetadata metadata = new SOBIMetadata();
                metadata.fileName = rs.getString("file_name");
                metadata.publishedDateTime = rs.getDate("published_date_time");
                metadata.processedDateTime = rs.getDate("processed_date_time");
                metadata.pendingProcessing = rs.getBoolean("pending_processing");
                metadata.processedCount = rs.getInt("processed_count");
                return metadata;
        if (sobiFile != null) {
            jdbcNamed.update(INSERT_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
        }
        else {
            throw new IllegalArgumentException("Supplied SOBIFile object is null. Cannot persist to database.");
        }
    private File getSOBIFileFromFileSystem(String fileName, Date publishedDate, boolean pendingProcessing) {
        return (pendingProcessing) ? getFileInWorkingDirectory(fileName) : getFileInArchiveDirectory(fileName, publishedDate);
    }

    /** Overload for convenience. @see #getSOBIFileFromFileSystem(String, java.util.Date, boolean) */
    private File getSOBIFileFromFileSystem(SOBIMetadata metaData) {
        return getSOBIFileFromFileSystem(metaData.fileName, metaData.publishedDateTime, metaData.pendingProcessing);
     * Deletes the actual SOBI file from the file system and writes the contents of SOBIFile to the appropriate directory.
     * @param sobiFile SOBIFile
     * @return File - Newly written file.
     * @throws IOException
    private File updateSOBIFileInFileSystem(SOBIFile sobiFile) throws IOException {
        File workFile = getFileInWorkingDirectory(sobiFile.getFileName());
        File archiveFile = getFileInArchiveDirectory(sobiFile.getFileName(), sobiFile.getPublishedDateTime());

        // Delete the file from any possible directory so we don't have to keep track of where it is.
        FileUtils.deleteQuietly(workFile);
        FileUtils.deleteQuietly(archiveFile);

        if (sobiFile.isPendingProcessing()) {
            // Write to the working directory.
            FileUtils.write(workFile, sobiFile.getText());
            return workFile;
        else {
            // Create any sub directories just to be safe and write to archive directory.
            archiveFile.getParentFile().mkdirs();
            FileUtils.write(archiveFile, sobiFile.getText());
            return archiveFile;
     * @param publishedDate Date
    private File getFileInArchiveDirectory(String fileName, Date publishedDate) {
        LocalDate localDate = new LocalDate(publishedDate);
        String year = Integer.toString(localDate.getYear());
     * Convenience method to convert a list of SOBIMetaData objects into SOBIFile objects.
     * @param metaDataList List<SOBIMetaData>
     * @return List<SOBIFile>
     */
    private List<SOBIFile> getSobiFileListFromMetaDataList(List<SOBIMetadata> metaDataList) {
        List<SOBIFile> sobiFiles = new ArrayList<>();
        try {
            for (SOBIMetadata metaData : metaDataList) {
                SOBIFile sobiFile = new SOBIFile(getSOBIFileFromFileSystem(metaData));
                metaData.applyToSOBIFile(sobiFile);
                sobiFiles.add(sobiFile);
            }
        }
        catch (IOException ex) {
            logger.error("Failed to construct SOBIFile instance!", ex);
            return null;
        }
        return sobiFiles;
    }

    /**
     * Returns a ParameterSource with columns mapped to SOBIFile values for use in update/insert queries.
}
SOBIFile sobiFile
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFileDao.java
sobiFile.setProcessedDateTime(this.processedDateTime);
            sobiFile.setPendingProcessing(this.pendingProcessing);
            sobiFile.setProcessedCount(this.processedCount);
        /** Row Mapper implementation */
        static class Mapper implements RowMapper<SOBIMetadata> {
            @Override
            public SOBIMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {
                SOBIMetadata metadata = new SOBIMetadata();
                metadata.fileName = rs.getString("file_name");
                metadata.publishedDateTime = rs.getDate("published_date_time");
                metadata.processedDateTime = rs.getDate("processed_date_time");
                metadata.pendingProcessing = rs.getBoolean("pending_processing");
                metadata.processedCount = rs.getInt("processed_count");
                return metadata;
        if (sobiFile != null) {
            jdbcNamed.update(INSERT_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
void applyToSOBIFile(SOBIFile sobiFile)
String fileName  Date publishedDate  boolean pendingProcessing
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFileDao.java
return (pendingProcessing) ? getFileInWorkingDirectory(fileName) : getFileInArchiveDirectory(fileName, publishedDate);
File getSOBIFileFromFileSystem(String fileName, Date publishedDate, boolean pendingProcessing)
SOBIMetadata metaData
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFileDao.java
return getSOBIFileFromFileSystem(metaData.fileName, metaData.publishedDateTime, metaData.pendingProcessing);
     * Deletes the actual SOBI file from the file system and writes the contents of SOBIFile to the appropriate directory.
     * @param sobiFile SOBIFile
     * @return File - Newly written file.
     * @throws IOException
    private File updateSOBIFileInFileSystem(SOBIFile sobiFile) throws IOException {
        File workFile = getFileInWorkingDirectory(sobiFile.getFileName());
        File archiveFile = getFileInArchiveDirectory(sobiFile.getFileName(), sobiFile.getPublishedDateTime());

        // Delete the file from any possible directory so we don't have to keep track of where it is.
        FileUtils.deleteQuietly(workFile);
        FileUtils.deleteQuietly(archiveFile);

        if (sobiFile.isPendingProcessing()) {
            // Write to the working directory.
            FileUtils.write(workFile, sobiFile.getText());
            return workFile;
        else {
            // Create any sub directories just to be safe and write to archive directory.
            archiveFile.getParentFile().mkdirs();
            FileUtils.write(archiveFile, sobiFile.getText());
            return archiveFile;
     * @param publishedDate Date
    private File getFileInArchiveDirectory(String fileName, Date publishedDate) {
        LocalDate localDate = new LocalDate(publishedDate);
        String year = Integer.toString(localDate.getYear());
     * Convenience method to convert a list of SOBIMetaData objects into SOBIFile objects.
     * @param metaDataList List<SOBIMetaData>
     * @return List<SOBIFile>
     */
    private List<SOBIFile> getSobiFileListFromMetaDataList(List<SOBIMetadata> metaDataList) {
        List<SOBIFile> sobiFiles = new ArrayList<>();
        try {
            for (SOBIMetadata metaData : metaDataList) {
                SOBIFile sobiFile = new SOBIFile(getSOBIFileFromFileSystem(metaData));
                metaData.applyToSOBIFile(sobiFile);
                sobiFiles.add(sobiFile);
File getSOBIFileFromFileSystem(SOBIMetadata metaData)
@@ -8,18 +8,17 @@
 import org.apache.log4j.Logger;
 import org.joda.time.LocalDate;
 import org.springframework.dao.DataAccessException;
-import org.springframework.dao.DataIntegrityViolationException;
-import org.springframework.dao.IncorrectResultSizeDataAccessException;
+import org.springframework.dao.DuplicateKeyException;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.*;
 
-import static gov.nysenate.openleg.util.FileHelper.moveFileToDirectory;
 import static gov.nysenate.openleg.util.FileHelper.safeListFiles;
 
 /**
@@ -65,12 +64,14 @@ public SqlSOBIFileDao(Environment environment) {
         "SELECT * FROM " + table(SOBI_FILE_TABLE) + " WHERE pending_processing = true ";
 
     private final String INSERT_SOBI_FILE =
-        "INSERT INTO " + table(SOBI_FILE_TABLE) + " (file_name, published_date_time, processed_date_time, processed_count, pending_processing) " +
-        "VALUES (:fileName, :publishedDateTime, :processedDateTime, :processedCount, :pendingProcessing)";
+        "INSERT INTO " + table(SOBI_FILE_TABLE) + "\n" +
+        "(file_name, published_date_time, staged_date_time, processed_date_time, processed_count, pending_processing) " +
+        "VALUES (:fileName, :publishedDateTime, :stagedDateTime, :processedDateTime, :processedCount, :pendingProcessing)";
 
     private final String UPDATE_SOBI_FILE =
         "UPDATE " + table(SOBI_FILE_TABLE) + "\n" +
         "SET published_date_time = :publishedDateTime," +
+        "    staged_date_time = :stagedDateTime," +
         "    processed_date_time = :processedDateTime," +
         "    pending_processing = :pendingProcessing," +
         "    processed_count = :processedCount " +
@@ -83,37 +84,17 @@ public SqlSOBIFileDao(Environment environment) {
     public SOBIFile getSOBIFile(String fileName) {
         SOBIFile sobiFile = null;
         MapSqlParameterSource params = new MapSqlParameterSource("fileNames", Arrays.asList(fileName));
-        try {
-            SOBIMetadata metaData = jdbcNamed.queryForObject(GET_SOBI_FILES_BY_FILE_NAMES_SQL, params, new SOBIMetadata.Mapper());
-            sobiFile = new SOBIFile(getSOBIFileFromFileSystem(metaData));
-            metaData.applyToSOBIFile(sobiFile);
-        }
-        catch (IncorrectResultSizeDataAccessException ex) {
-            logger.debug("No sobi details were found in database for " + fileName, ex);
-        }
-        catch (IOException ex) {
-            logger.error("Possible inconsistency between file system and database! " +
-                         "Failed to construct SOBIFile instance with file name " + fileName, ex);
-        }
-        return sobiFile;
+        return jdbcNamed.queryForObject(GET_SOBI_FILES_BY_FILE_NAMES_SQL, params, new SOBIFileRowMapper());
     }
 
     /** {@inheritDoc }*/
     @Override
     public Map<String, SOBIFile> getSOBIFiles(List<String> fileNames) {
         MapSqlParameterSource params = new MapSqlParameterSource("fileNames", fileNames);
         Map<String, SOBIFile> sobiFileMap = new HashMap<>();
-        List<SOBIMetadata> metaDataList = jdbcNamed.query(GET_SOBI_FILES_BY_FILE_NAMES_SQL, params, new SOBIMetadata.Mapper());
-        try {
-            for (SOBIMetadata metaData : metaDataList) {
-                SOBIFile sobiFile = new SOBIFile(getSOBIFileFromFileSystem(metaData));
-                metaData.applyToSOBIFile(sobiFile);
-                sobiFileMap.put(sobiFile.getFileName(), sobiFile);
-            }
-        }
-        catch (IOException ex) {
-            logger.error("Failed to construct SOBIFile instance!", ex);
-            return null;
+        List<SOBIFile> sobiList = jdbcNamed.query(GET_SOBI_FILES_BY_FILE_NAMES_SQL, params, new SOBIFileRowMapper());
+        for (SOBIFile sobiFile : sobiList) {
+            sobiFileMap.put(sobiFile.getFileName(), sobiFile);
         }
         return sobiFileMap;
     }
@@ -125,63 +106,51 @@ public List<SOBIFile> getSOBIFilesDuring(Date start, Date end, boolean processed
         params.addValue("startDate", start);
         params.addValue("endDate", end);
         params.addValue("processedOnly", processedOnly);
-        List<SOBIMetadata> metaDataList = jdbcNamed.query(GET_SOBI_FILES_DURING_SQL + orderBy("published_date_time", sortByPubDate),
-                                                          params, new SOBIMetadata.Mapper());
-        return getSobiFileListFromMetaDataList(metaDataList);
+        return jdbcNamed.query(GET_SOBI_FILES_DURING_SQL + orderBy("published_date_time", sortByPubDate),
+                params, new SOBIFileRowMapper());
     }
 
     /** {@inheritDoc }*/
     @Override
     public List<SOBIFile> getPendingSOBIFiles(SortOrder sortByPubDate) {
-        List<SOBIMetadata> metaDataList =
-            jdbc.query(GET_PENDING_SOBI_FILES_SQL + orderBy("published_date_time", sortByPubDate), new SOBIMetadata.Mapper());
-        return getSobiFileListFromMetaDataList(metaDataList);
+        return jdbc.query(GET_PENDING_SOBI_FILES_SQL + orderBy("published_date_time", sortByPubDate),                new SOBIFileRowMapper());
     }
 
     /** {@inheritDoc }*/
     @Override
     public void stageSOBIFiles(boolean allowReStaging) throws IOException {
         logger.info("Performing staging of new SOBI files...");
         int filesStaged = 0;
-
         for (File file : safeListFiles(this.stagingSobiDir, null, false)) {
             SOBIFile sobiFile = new SOBIFile(file);
+            sobiFile.setStagedDateTime(new Date());
             sobiFile.setPendingProcessing(true);
             try {
                 insertSOBIMetadataInDatabase(sobiFile);
-                if (!file.delete()) {
-                    logger.fatal("Failed to delete " + file.getAbsoluteFile() + " from the sobi staging directory!");
-                }
-                updateSOBIFileInFileSystem(sobiFile);
+                moveFileToWorking(file);
                 filesStaged++;
                 logger.debug("Staged " + sobiFile);
             }
-            catch(DataIntegrityViolationException ex) {
+            catch (DuplicateKeyException ex) {
                 if (allowReStaging) {
                     // The SOBI file has been staged once before so we want to re-stage it with the contents of the
                     // new file which may or may not be identical. This should rarely occur in production but can
                     // be helpful during development.
                     SOBIFile existingSOBIFile = getSOBIFile(sobiFile.getFileName());
                     if (existingSOBIFile != null) {
-                        File existingFile = getSOBIFileFromFileSystem(existingSOBIFile);
-                        if (existingFile.exists()) {
-                            moveFileToDirectory(file, this.workingSobiDir, true);
-                            existingSOBIFile.setPendingProcessing(true);
-                            updateSOBIFile(existingSOBIFile);
-                            filesStaged++;
-                            logger.debug("Re-staged " + existingSOBIFile + " with latest file contents.");
-                        }
-                        else {
-                            logger.warn("Attempted to re-stage " + existingSOBIFile + " but the file " +
-                                    existingFile.getAbsolutePath() + " doesn't exist!");
-                        }
+                        existingSOBIFile.setStagedDateTime(new Date());
+                        existingSOBIFile.setPendingProcessing(true);
+                        updateSOBIFile(existingSOBIFile);
+                        moveFileToWorking(file);
+                        filesStaged++;
+                        logger.debug("Re-staged " + existingSOBIFile + " with latest file contents.");
                     }
                     else {
                         logger.warn("Failed to stage " + sobiFile + " because a prior record exists but cannot be retrieved!");
                     }
                 }
                 else {
-                    logger.debug("Skipping " + sobiFile + " since re-staging is disabled.");
+                    logger.debug("Ignoring " + sobiFile + " since re-staging is disabled and file was previously processed.");
                 }
             }
         }
@@ -193,9 +162,14 @@ public void stageSOBIFiles(boolean allowReStaging) throws IOException {
     public boolean updateSOBIFile(SOBIFile sobiFile) {
         if (sobiFile != null) {
             try {
-                int affected = jdbcNamed.update(UPDATE_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
+                int affected = updateSOBIMetadataInDatabase(sobiFile);
                 if (affected > 0) {
-                    updateSOBIFileInFileSystem(sobiFile);
+                    if (!sobiFile.isPendingProcessing()) {
+                        File workFile = getFileInWorkingDirectory(sobiFile.getFileName());
+                        if (workFile.exists()) {
+                            moveFiletoArchive(workFile, sobiFile.getPublishedDateTime());
+                        }
+                    }
                     return true;
                 }
                 return false;
@@ -226,47 +200,58 @@ public void deleteAll() throws IOException {
 
     /** --- Helper Classes --- */
 
-    /** Models the processing details for a sobi file which is stored in the database */
-    protected static class SOBIMetadata {
-        String fileName;
-        Date publishedDateTime;
-        Date processedDateTime;
-        boolean pendingProcessing;
-        int processedCount;
-
-        /** Apply metadata info to SOBIFile */
-        void applyToSOBIFile(SOBIFile sobiFile) {
-            sobiFile.setProcessedDateTime(this.processedDateTime);
-            sobiFile.setPendingProcessing(this.pendingProcessing);
-            sobiFile.setProcessedCount(this.processedCount);
+    /**
+     * Maps rows from the sobi table to SOBIFile objects.
+     */
+    protected class SOBIFileRowMapper implements RowMapper<SOBIFile> {
+        private String pfx = "";
+
+        public SOBIFileRowMapper(){}
+        public SOBIFileRowMapper(String pfx) {
+            this.pfx = pfx;
         }
 
-        /** Row Mapper implementation */
-        static class Mapper implements RowMapper<SOBIMetadata> {
-            @Override
-            public SOBIMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {
-                SOBIMetadata metadata = new SOBIMetadata();
-                metadata.fileName = rs.getString("file_name");
-                metadata.publishedDateTime = rs.getDate("published_date_time");
-                metadata.processedDateTime = rs.getDate("processed_date_time");
-                metadata.pendingProcessing = rs.getBoolean("pending_processing");
-                metadata.processedCount = rs.getInt("processed_count");
-                return metadata;
+        @Override
+        public SOBIFile mapRow(ResultSet rs, int rowNum) throws SQLException {
+            String fileName = rs.getString(pfx + "file_name");
+            Date publishedDateTime = rs.getDate(pfx + "published_date_time");
+            boolean pendingProcessing = rs.getBoolean(pfx + "pending_processing");
+            File file = getSOBIFileFromFileSystem(fileName, publishedDateTime, pendingProcessing);
+            try {
+                SOBIFile sobiFile = new SOBIFile(file);
+                sobiFile.setPendingProcessing(pendingProcessing);
+                sobiFile.setProcessedDateTime(rs.getDate(pfx + "processed_date_time"));
+                sobiFile.setProcessedCount(rs.getInt(pfx + "processed_count"));
+                return sobiFile;
             }
+            catch (FileNotFoundException ex) {
+                logger.error("SOBI file " + rs.getString("file_name") + " was not found in the expected location! \n" +
+                        "This could be a result of modifications to the sobi file directory that were not synced with " +
+                        "the database. ", ex);
+            }
+            catch (IOException ex) {
+                logger.error(ex);
+            }
+            return null;
         }
     }
 
+    /** --- Internal Methods --- */
+
     /**
      * Saves the metadata of the SOBIFile into the database.
      * @throws java.lang.IllegalArgumentException if sobiFile is null
      */
     private void insertSOBIMetadataInDatabase(SOBIFile sobiFile) throws DataAccessException {
-        if (sobiFile != null) {
-            jdbcNamed.update(INSERT_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
-        }
-        else {
-            throw new IllegalArgumentException("Supplied SOBIFile object is null. Cannot persist to database.");
-        }
+        jdbcNamed.update(INSERT_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
+    }
+
+    /**
+     * Updates the metadata of the SOBIFile into the database.
+     * @throws java.lang.IllegalArgumentException if sobiFile is null
+     */
+    private int updateSOBIMetadataInDatabase(SOBIFile sobiFile) throws DataAccessException {
+        return jdbcNamed.update(UPDATE_SOBI_FILE, getSOBIInsertUpdateParams(sobiFile));
     }
 
     /**
@@ -276,13 +261,8 @@ private void insertSOBIMetadataInDatabase(SOBIFile sobiFile) throws DataAccessEx
      * @param pendingProcessing boolean - Indicates where to look for the SOBI file.
      * @return File
      */
-    private File getSOBIFileFromFileSystem(String fileName, Date publishedDate, boolean pendingProcessing) {
-        return (pendingProcessing) ? getFileInWorkingDirectory(fileName) : getFileInArchiveDirectory(fileName, publishedDate);
-    }
-
-    /** Overload for convenience. @see #getSOBIFileFromFileSystem(String, java.util.Date, boolean) */
-    private File getSOBIFileFromFileSystem(SOBIMetadata metaData) {
-        return getSOBIFileFromFileSystem(metaData.fileName, metaData.publishedDateTime, metaData.pendingProcessing);
+    private File getSOBIFileFromFileSystem(String fileName, Date publishedDateTime, boolean pendingProcessing) {
+        return (pendingProcessing) ? getFileInWorkingDirectory(fileName) : getFileInArchiveDirectory(fileName, publishedDateTime);
     }
 
     /** Overload for convenience. @see #getSOBIFileFromFileSystem(String, java.util.Date, boolean) */
@@ -291,30 +271,37 @@ private File getSOBIFileFromFileSystem(SOBIFile sobiFile) {
     }
 
     /**
-     * Deletes the actual SOBI file from the file system and writes the contents of SOBIFile to the appropriate directory.
-     * @param sobiFile SOBIFile
-     * @return File - Newly written file.
-     * @throws IOException
+     * Moves the file into the working SOBI file directory.
+     * @param file File
      */
-    private File updateSOBIFileInFileSystem(SOBIFile sobiFile) throws IOException {
-        File workFile = getFileInWorkingDirectory(sobiFile.getFileName());
-        File archiveFile = getFileInArchiveDirectory(sobiFile.getFileName(), sobiFile.getPublishedDateTime());
-
-        // Delete the file from any possible directory so we don't have to keep track of where it is.
-        FileUtils.deleteQuietly(workFile);
-        FileUtils.deleteQuietly(archiveFile);
-
-        if (sobiFile.isPendingProcessing()) {
-            // Write to the working directory.
-            FileUtils.write(workFile, sobiFile.getText());
-            return workFile;
+    private void moveFileToWorking(File file) throws IOException {
+        File workingFile = getFileInWorkingDirectory(file.getName());
+        if (workingFile.exists()) {
+            FileUtils.deleteQuietly(workingFile);
         }
-        else {
-            // Create any sub directories just to be safe and write to archive directory.
-            archiveFile.getParentFile().mkdirs();
-            FileUtils.write(archiveFile, sobiFile.getText());
-            return archiveFile;
+        FileUtils.moveFile(file, workingFile);
+    }
+
+    /**
+     * Moves the file into the archive SOBI file directory.
+     * @param file File
+     */
+    private void moveFiletoArchive(File file, Date publishedDateTime) throws IOException {
+        File archiveFile = getFileInArchiveDirectory(file.getName(), publishedDateTime);
+        if (archiveFile.exists()) {
+            FileUtils.deleteQuietly(archiveFile);
         }
+        FileUtils.moveFile(file, archiveFile);
+    }
+
+    /**
+     * Delete any traces of the SOBI file (either in work or archive directory) from the file system.
+     * @param fileName String
+     * @param publishedDateTime Date
+     */
+    private void deleteSOBIFileFromFileSystem(String fileName, Date publishedDateTime) {
+        FileUtils.deleteQuietly(getFileInWorkingDirectory(fileName));
+        FileUtils.deleteQuietly(getFileInArchiveDirectory(fileName, publishedDateTime));
     }
 
     /**
@@ -329,39 +316,17 @@ private File getFileInWorkingDirectory(String fileName) {
     /**
      * Get file handle from the sobi archive directory.
      * @param fileName String
-     * @param publishedDate Date
+     * @param publishedDateTime Date
      * @return File
      */
-    private File getFileInArchiveDirectory(String fileName, Date publishedDate) {
-        LocalDate localDate = new LocalDate(publishedDate);
-        String year = Integer.toString(localDate.getYear());
+    private File getFileInArchiveDirectory(String fileName, Date publishedDateTime) {
+        String year = Integer.toString(new LocalDate(publishedDateTime).getYear());
         File dir = new File(this.archiveSobiDir, year);
         return new File(dir, fileName);
     }
 
     /**
-     * Convenience method to convert a list of SOBIMetaData objects into SOBIFile objects.
-     * @param metaDataList List<SOBIMetaData>
-     * @return List<SOBIFile>
-     */
-    private List<SOBIFile> getSobiFileListFromMetaDataList(List<SOBIMetadata> metaDataList) {
-        List<SOBIFile> sobiFiles = new ArrayList<>();
-        try {
-            for (SOBIMetadata metaData : metaDataList) {
-                SOBIFile sobiFile = new SOBIFile(getSOBIFileFromFileSystem(metaData));
-                metaData.applyToSOBIFile(sobiFile);
-                sobiFiles.add(sobiFile);
-            }
-        }
-        catch (IOException ex) {
-            logger.error("Failed to construct SOBIFile instance!", ex);
-            return null;
-        }
-        return sobiFiles;
-    }
-
-    /**
-     * Returns a ParameterSource with columns mapped to SOBIFile values for use in update/insert queries.
+     * Returns a MapSqlParameterSource with columns mapped to SOBIFile values for use in update/insert queries.
      * @param sobiFile SOBIFile
      * @return MapSqlParameterSource
      */
@@ -370,8 +335,9 @@ private MapSqlParameterSource getSOBIInsertUpdateParams(SOBIFile sobiFile) {
         params.addValue("fileName", sobiFile.getFileName());
         params.addValue("publishedDateTime", toTimestamp(sobiFile.getPublishedDateTime()));
         params.addValue("processedDateTime", toTimestamp(sobiFile.getProcessedDateTime()));
+        params.addValue("stagedDateTime", toTimestamp(sobiFile.getStagedDateTime()));
         params.addValue("processedCount", sobiFile.getProcessedCount());
         params.addValue("pendingProcessing", sobiFile.isPendingProcessing());
         return params;
     }
-}
+}
\ No newline at end of file
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSqlSOBIFileDao.java
be1d2c8aa127e1ffdbcc28f554f3c4eb5bc85656
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSqlSOBIFragmentDao.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSqlSOBIFragmentDao.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFragmentDao.java
import gov.nysenate.openleg.dao.base.SortOrder;
import gov.nysenate.openleg.model.sobi.SOBIFile;
import gov.nysenate.openleg.model.sobi.SOBIFragmentType;
import org.apache.commons.io.FileUtils;
import org.joda.time.LocalDate;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;

import java.io.File;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
    protected static final String SOBI_FRAGMENT_TABLE = "sobi_fragment";

    /** --- SQL Statements --- */

    private final String CHECK_SOBI_FRAGMENT_EXISTS_SQL =
        "SELECT 1 FROM " + table(SOBI_FRAGMENT_TABLE) + "\n" +
        "WHERE fragment_file_name = :fragmentFileName";

    private final String GET_SOBI_FRAGMENT_BY_FILE_NAME_SQL =
        "SELECT * FROM " + table(SOBI_FRAGMENT_TABLE) + "\n" +
        "WHERE sobi_fragment_type = :sobiFragmentType AND fragment_file_name = :fileName";

    private final String GET_SOBI_FRAGMENTS_BY_SOBI_FILE_SQL =
        "SELECT * FROM " + table(SOBI_FRAGMENT_TABLE) + " WHERE sobi_file_name = :sobiFileName ";

    private final String GET_SOBI_FRAGMENTS_BY_SOBI_FILE_FILTERED_SQL =
        "SELECT * FROM " + table(SOBI_FRAGMENT_TABLE) + "\n" +
        "WHERE sobi_file_name = :sobiFileName AND sobi_fragment_type = :sobiFragmentType ";

    private final String UPDATE_SOBI_FRAGMENT_SQL =
        "UPDATE " + table(SOBI_FRAGMENT_TABLE) + "\n" +
        "SET sobi_file_name = :sobiFileName, published_date_time = :publishedDateTime, " +
        "    sobi_fragment_type = :sobiFragmentType, file_counter = :fileCounter, text = :text\n" +
        "WHERE fragment_file_name = :fragmentFileName";

    private final String INSERT_SOBI_FRAGMENT_SQL =
        "INSERT INTO " + table(SOBI_FRAGMENT_TABLE) +
        " (sobi_file_name, fragment_file_name, published_date_time, sobi_fragment_type, file_counter, text)" +
        " VALUES (:sobiFileName, :fragmentFileName, :publishedDateTime, :sobiFragmentType, :fileCounter, :text)";

    private final String DELETE_SOBI_FRAGMENTS_SQL =
        "DELETE FROM " + table(SOBI_FRAGMENT_TABLE) + " WHERE sobi_file_name = :sobiFileName";

    /** --- Constructors --- */
    /** --- Implemented Methods --- */

    public SOBIFragment getSOBIFragment(SOBIFragmentType fragmentType, String fragmentFileName) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("sobiFragmentType", fragmentType.name());
        params.addValue("fileName", fragmentFileName);
        return jdbcNamed.queryForObject(GET_SOBI_FRAGMENT_BY_FILE_NAME_SQL, params, new SOBIFragmentRowMapper());
    /** {@inheritDoc} */
    public List<SOBIFragment> getSOBIFragments(SOBIFile sobiFile, SortOrder order) {
        MapSqlParameterSource params = new MapSqlParameterSource("sobiFileName", sobiFile.getFileName());
        return jdbcNamed.query(GET_SOBI_FRAGMENTS_BY_SOBI_FILE_SQL + orderBy("fragment_file_name", order), params,
                               new SOBIFragmentRowMapper(sobiFile));
    }

    /** {@inheritDoc} */
    @Override
    public List<SOBIFragment> getSOBIFragments(SOBIFile sobiFile, SOBIFragmentType fragmentType, SortOrder order) {
        MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("sobiFileName", sobiFile.getFileName());
        params.addValue("sobiFragmentType", fragmentType.name());
        return jdbcNamed.query(GET_SOBI_FRAGMENTS_BY_SOBI_FILE_FILTERED_SQL + orderBy("fragment_file_name", order),
                              params, new SOBIFragmentRowMapper(sobiFile));
    }

    /** {@inheritDoc} */
    @Override
    public void saveSOBIFragments(List<SOBIFragment> fragments) {
        for (SOBIFragment fragment : fragments) {
            // Set a unique file name for this fragment
            fragment.setFileName(createFileNameForFragment(fragment));
            // Persist the meta data of the fragment in the database
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("sobiFileName", fragment.getParentSOBIFile().getFileName());
            params.addValue("fragmentFileName", fragment.getFileName());
            params.addValue("publishedDateTime", toTimestamp(fragment.getPublishedDateTime()));
            params.addValue("sobiFragmentType", fragment.getType().name());
            params.addValue("fileCounter", fragment.getCounter());
            params.addValue("text", fragment.getText().replace('\0', ' '));
            if (jdbcNamed.update(UPDATE_SOBI_FRAGMENT_SQL, params) == 0) {
                jdbcNamed.update(INSERT_SOBI_FRAGMENT_SQL, params);
            }
            logger.debug("Saved sobi fragment " + fragment.getFileName());
        }
    }

    /** {@inheritDoc} */
    @Override
    public void deleteSOBIFragments(SOBIFile sobiFile) {
        MapSqlParameterSource params = new MapSqlParameterSource("sobiFileName", sobiFile.getFileName());
        jdbcNamed.update(DELETE_SOBI_FRAGMENTS_SQL, params);
    }

    /** --- Helper Classes --- */

    /**
     * Maps rows from the sobi fragment table to SOBIFragment objects.
     */
    protected class SOBIFragmentRowMapper implements RowMapper<SOBIFragment> {
        private String pfx = "";
        private Map<String, SOBIFile> sobiFileMap = new HashMap<>();

        public SOBIFragmentRowMapper() {
            this("", Collections.<SOBIFile>emptyList());
        }

        public SOBIFragmentRowMapper(SOBIFile sobiFile) {
             this("", Arrays.asList(sobiFile));
        }

        public SOBIFragmentRowMapper(String pfx, List<SOBIFile> sobiFiles) {
            this.pfx = pfx;
            for (SOBIFile sobiFile : sobiFiles) {
                this.sobiFileMap.put(sobiFile.getFileName(), sobiFile);
            }
        }

        @Override
        public SOBIFragment mapRow(ResultSet rs, int rowNum) throws SQLException {
            String sobiFileName = rs.getString(pfx + "sobi_file_name");
            // Passing the sobi file objects in the constructor is a means of caching the objects
            // so that they don't have to be re-mapped. If not supplied, the underlying sobi file dao
            // will need to be utilized.
            SOBIFile sobiFile = this.sobiFileMap.get(sobiFileName);
            if (sobiFile == null) {
                sobiFile = getSOBIFile(sobiFileName);
            }
            String fileName = rs.getString(pfx + "fragment_file_name");
            SOBIFragmentType type = SOBIFragmentType.valueOf(rs.getString(pfx + "sobi_fragment_type"));
            int counter = rs.getInt(pfx + "file_counter");
            String text = rs.getString(pfx + "text");
            SOBIFragment fragment = new SOBIFragment(sobiFile, type, text, counter);
            fragment.setFileName(fileName);
            return fragment;
        }
    }

    /** --- Internal Methods --- */
    /**
     * Generates unique file names for the fragments.
     * E.g SOBI.20140101.000001-bill-1.sobi, SOBI.20140101.000001-agenda-2.sobi
     * @param fragment SOBIFragment
     * @return String
     */
    private String createFileNameForFragment(SOBIFragment fragment) {
        return fragment.getParentSOBIFile().getFileName() + "-" +
               fragment.getType().name().toLowerCase() + "-" + fragment.getCounter() +
               ((fragment.getType().isXml()) ? ".xml" : ".sobi");
SOBIFragmentType fragmentType  String fragmentFileName
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFragmentDao.java
MapSqlParameterSource params = new MapSqlParameterSource();
        params.addValue("sobiFragmentType", fragmentType.name());
        params.addValue("fileName", fragmentFileName);
        return jdbcNamed.queryForObject(GET_SOBI_FRAGMENT_BY_FILE_NAME_SQL, params, new SOBIFragmentRowMapper());
    /** {@inheritDoc
SOBIFragment getSOBIFragment(SOBIFragmentType fragmentType, String fragmentFileName)
List<SOBIFragment> fragments
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFragmentDao.java
for (SOBIFragment fragment : fragments) {
            // Set a unique file name for this fragment
            fragment.setFileName(createFileNameForFragment(fragment));
            // Persist the meta data of the fragment in the database
            MapSqlParameterSource params = new MapSqlParameterSource();
            params.addValue("sobiFileName", fragment.getParentSOBIFile().getFileName());
            params.addValue("fragmentFileName", fragment.getFileName());
            params.addValue("publishedDateTime", toTimestamp(fragment.getPublishedDateTime()));
            params.addValue("sobiFragmentType", fragment.getType().name());
            params.addValue("fileCounter", fragment.getCounter());
            params.addValue("text", fragment.getText().replace('\0', ' '));
            if (jdbcNamed.update(UPDATE_SOBI_FRAGMENT_SQL, params) == 0) {
                jdbcNamed.update(INSERT_SOBI_FRAGMENT_SQL, params);
void saveSOBIFragments(List<SOBIFragment> fragments)
SOBIFile sobiFile
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFragmentDao.java
MapSqlParameterSource params = new MapSqlParameterSource("sobiFileName", sobiFile.getFileName());
        jdbcNamed.update(DELETE_SOBI_FRAGMENTS_SQL, params);
void deleteSOBIFragments(SOBIFile sobiFile)
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFragmentDao.java
this("", Collections.<SOBIFile>emptyList());
public SOBIFragmentRowMapper()
SOBIFile sobiFile
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFragmentDao.java
this("", Arrays.asList(sobiFile));
public SOBIFragmentRowMapper(SOBIFile sobiFile)
String pfx  List<SOBIFile> sobiFiles
src/main/java/gov/nysenate/openleg/dao/sobi/SqlSOBIFragmentDao.java
this.pfx = pfx;
            for (SOBIFile sobiFile : sobiFiles) {
                this.sobiFileMap.put(sobiFile.getFileName(), sobiFile);
public SOBIFragmentRowMapper(String pfx, List<SOBIFile> sobiFiles)
    private static String SOBI_FRAGMENT_TABLE = "sobi_fragment";
    public SOBIFragment getSOBIFragment(String fragmentFileName) {
        return null;
    public void saveSOBIFragment(SOBIFragment fragment) {
@@ -1,27 +1,178 @@
 package gov.nysenate.openleg.dao.sobi;
 
 import gov.nysenate.openleg.Environment;
+import gov.nysenate.openleg.dao.base.SortOrder;
+import gov.nysenate.openleg.model.sobi.SOBIFile;
 import gov.nysenate.openleg.model.sobi.SOBIFragment;
+import gov.nysenate.openleg.model.sobi.SOBIFragmentType;
+import org.apache.commons.io.FileUtils;
 import org.apache.log4j.Logger;
+import org.joda.time.LocalDate;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
 
 public class SqlSOBIFragmentDao extends SqlSOBIFileDao implements SOBIFragmentDao
 {
     private static Logger logger = Logger.getLogger(SqlSOBIFragmentDao.class);
 
     /** The database table where SOBI fragments are recorded */
-    private static String SOBI_FRAGMENT_TABLE = "sobi_fragment";
+    protected static final String SOBI_FRAGMENT_TABLE = "sobi_fragment";
+
+    /** --- SQL Statements --- */
+
+    private final String CHECK_SOBI_FRAGMENT_EXISTS_SQL =
+        "SELECT 1 FROM " + table(SOBI_FRAGMENT_TABLE) + "\n" +
+        "WHERE fragment_file_name = :fragmentFileName";
+
+    private final String GET_SOBI_FRAGMENT_BY_FILE_NAME_SQL =
+        "SELECT * FROM " + table(SOBI_FRAGMENT_TABLE) + "\n" +
+        "WHERE sobi_fragment_type = :sobiFragmentType AND fragment_file_name = :fileName";
+
+    private final String GET_SOBI_FRAGMENTS_BY_SOBI_FILE_SQL =
+        "SELECT * FROM " + table(SOBI_FRAGMENT_TABLE) + " WHERE sobi_file_name = :sobiFileName ";
+
+    private final String GET_SOBI_FRAGMENTS_BY_SOBI_FILE_FILTERED_SQL =
+        "SELECT * FROM " + table(SOBI_FRAGMENT_TABLE) + "\n" +
+        "WHERE sobi_file_name = :sobiFileName AND sobi_fragment_type = :sobiFragmentType ";
+
+    private final String UPDATE_SOBI_FRAGMENT_SQL =
+        "UPDATE " + table(SOBI_FRAGMENT_TABLE) + "\n" +
+        "SET sobi_file_name = :sobiFileName, published_date_time = :publishedDateTime, " +
+        "    sobi_fragment_type = :sobiFragmentType, file_counter = :fileCounter, text = :text\n" +
+        "WHERE fragment_file_name = :fragmentFileName";
+
+    private final String INSERT_SOBI_FRAGMENT_SQL =
+        "INSERT INTO " + table(SOBI_FRAGMENT_TABLE) +
+        " (sobi_file_name, fragment_file_name, published_date_time, sobi_fragment_type, file_counter, text)" +
+        " VALUES (:sobiFileName, :fragmentFileName, :publishedDateTime, :sobiFragmentType, :fileCounter, :text)";
+
+    private final String DELETE_SOBI_FRAGMENTS_SQL =
+        "DELETE FROM " + table(SOBI_FRAGMENT_TABLE) + " WHERE sobi_file_name = :sobiFileName";
+
+    /** --- Constructors --- */
 
     public SqlSOBIFragmentDao(Environment environment) {
         super(environment);
     }
 
+    /** --- Implemented Methods --- */
+
     @Override
-    public SOBIFragment getSOBIFragment(String fragmentFileName) {
-        return null;
+    public SOBIFragment getSOBIFragment(SOBIFragmentType fragmentType, String fragmentFileName) {
+        MapSqlParameterSource params = new MapSqlParameterSource();
+        params.addValue("sobiFragmentType", fragmentType.name());
+        params.addValue("fileName", fragmentFileName);
+        return jdbcNamed.queryForObject(GET_SOBI_FRAGMENT_BY_FILE_NAME_SQL, params, new SOBIFragmentRowMapper());
     }
 
+    /** {@inheritDoc} */
     @Override
-    public void saveSOBIFragment(SOBIFragment fragment) {
+    public List<SOBIFragment> getSOBIFragments(SOBIFile sobiFile, SortOrder order) {
+        MapSqlParameterSource params = new MapSqlParameterSource("sobiFileName", sobiFile.getFileName());
+        return jdbcNamed.query(GET_SOBI_FRAGMENTS_BY_SOBI_FILE_SQL + orderBy("fragment_file_name", order), params,
+                               new SOBIFragmentRowMapper(sobiFile));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public List<SOBIFragment> getSOBIFragments(SOBIFile sobiFile, SOBIFragmentType fragmentType, SortOrder order) {
+        MapSqlParameterSource params = new MapSqlParameterSource();
+        params.addValue("sobiFileName", sobiFile.getFileName());
+        params.addValue("sobiFragmentType", fragmentType.name());
+        return jdbcNamed.query(GET_SOBI_FRAGMENTS_BY_SOBI_FILE_FILTERED_SQL + orderBy("fragment_file_name", order),
+                              params, new SOBIFragmentRowMapper(sobiFile));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void saveSOBIFragments(List<SOBIFragment> fragments) {
+        for (SOBIFragment fragment : fragments) {
+            // Set a unique file name for this fragment
+            fragment.setFileName(createFileNameForFragment(fragment));
+            // Persist the meta data of the fragment in the database
+            MapSqlParameterSource params = new MapSqlParameterSource();
+            params.addValue("sobiFileName", fragment.getParentSOBIFile().getFileName());
+            params.addValue("fragmentFileName", fragment.getFileName());
+            params.addValue("publishedDateTime", toTimestamp(fragment.getPublishedDateTime()));
+            params.addValue("sobiFragmentType", fragment.getType().name());
+            params.addValue("fileCounter", fragment.getCounter());
+            params.addValue("text", fragment.getText().replace('\0', ' '));
+            if (jdbcNamed.update(UPDATE_SOBI_FRAGMENT_SQL, params) == 0) {
+                jdbcNamed.update(INSERT_SOBI_FRAGMENT_SQL, params);
+            }
+            logger.debug("Saved sobi fragment " + fragment.getFileName());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void deleteSOBIFragments(SOBIFile sobiFile) {
+        MapSqlParameterSource params = new MapSqlParameterSource("sobiFileName", sobiFile.getFileName());
+        jdbcNamed.update(DELETE_SOBI_FRAGMENTS_SQL, params);
+    }
+
+    /** --- Helper Classes --- */
+
+    /**
+     * Maps rows from the sobi fragment table to SOBIFragment objects.
+     */
+    protected class SOBIFragmentRowMapper implements RowMapper<SOBIFragment> {
+        private String pfx = "";
+        private Map<String, SOBIFile> sobiFileMap = new HashMap<>();
+
+        public SOBIFragmentRowMapper() {
+            this("", Collections.<SOBIFile>emptyList());
+        }
+
+        public SOBIFragmentRowMapper(SOBIFile sobiFile) {
+             this("", Arrays.asList(sobiFile));
+        }
+
+        public SOBIFragmentRowMapper(String pfx, List<SOBIFile> sobiFiles) {
+            this.pfx = pfx;
+            for (SOBIFile sobiFile : sobiFiles) {
+                this.sobiFileMap.put(sobiFile.getFileName(), sobiFile);
+            }
+        }
+
+        @Override
+        public SOBIFragment mapRow(ResultSet rs, int rowNum) throws SQLException {
+            String sobiFileName = rs.getString(pfx + "sobi_file_name");
+            // Passing the sobi file objects in the constructor is a means of caching the objects
+            // so that they don't have to be re-mapped. If not supplied, the underlying sobi file dao
+            // will need to be utilized.
+            SOBIFile sobiFile = this.sobiFileMap.get(sobiFileName);
+            if (sobiFile == null) {
+                sobiFile = getSOBIFile(sobiFileName);
+            }
+            String fileName = rs.getString(pfx + "fragment_file_name");
+            SOBIFragmentType type = SOBIFragmentType.valueOf(rs.getString(pfx + "sobi_fragment_type"));
+            int counter = rs.getInt(pfx + "file_counter");
+            String text = rs.getString(pfx + "text");
+            SOBIFragment fragment = new SOBIFragment(sobiFile, type, text, counter);
+            fragment.setFileName(fileName);
+            return fragment;
+        }
+    }
+
+    /** --- Internal Methods --- */
 
+    /**
+     * Generates unique file names for the fragments.
+     * E.g SOBI.20140101.000001-bill-1.sobi, SOBI.20140101.000001-agenda-2.sobi
+     * @param fragment SOBIFragment
+     * @return String
+     */
+    private String createFileNameForFragment(SOBIFragment fragment) {
+        return fragment.getParentSOBIFile().getFileName() + "-" +
+               fragment.getType().name().toLowerCase() + "-" + fragment.getCounter() +
+               ((fragment.getType().isXml()) ? ".xml" : ".sobi");
     }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fdao%2Fsobi%2FSqlSOBIFragmentDao.java
98764c56d5fb38066759f4a2038854427a21ba38
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2FBaseObject.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2FBaseObject.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/BaseObject.java
    /** True if the object was not retrieved from storage. */
    /** The date the object was most recently modified. */
    /** The date the object was most recently published. */
    /** The session this object was created in. */
    /** The calendar year this object was active in. */
    /** A set of SOBI files that contained modifications to this object. */
    /** Initializes BaseObject parameters. */
    /** --- Functional Getters/Setters --- */
     * Add a new source to the set of data sources. This is preferred to
     * getting a reference to the list and directly adding filenames
     *
     * @param source - The new source to add to the data sources.
    public void addDataSource(String source) {
        dataSources.add(source);
    }

    /** --- Basic Getters/Setters --- */
    public Date getPublishDate() {
    public void setPublishDate(Date publishDate) {
    public boolean isPublished() {
String source
src/main/java/gov/nysenate/openleg/model/BaseObject.java
dataSources.add(source);
void addDataSource(String source)
 *
 * Does NOT implement getOid() or getOType(). All base classes are responsible for forming their
 * own unique object IDs and object types.
 *
    /**
     * True if the object was not retrieved from storage.
     */
    /**
     * The date the object was most recently modified
     */
    /**
     * The date the object was most recently published
     */
    /**
     * The session this object was created in.
     */
    /**
     * The calendar year this object was active in.
     */
    /**
     * A set of SOBI files that contained modifications to this object.
     */
    /**
     * Initializes BaseObject parameters.
     */
    /**
     * Get the unique id for this object for Lucene/web indexing and purging.
     */
    abstract public String getOid();
     * Get the document type for this object.
    abstract public String getOtype();
    /**
     * @return - True if the object was not retrieved from storage.
     */
    /**
     * @param brandNew - The new brandNew state.
     */
    /**
     * @return - The last publish date.
     */
    public Date getPublishDate()
    {
    /**
     * Set the last publish date.
     *
     * @param publishDate - The new publish date.
     */
    public void setPublishDate(Date publishDate)
    {
    /**
     * @return - true if the object has been published
     */
    public boolean isPublished()
    {
    /**
     * Gets the last modified time in milliseconds since epoch.
     */
    /**
     * Sets the last modified timestamp in milliseconds since epoch.
     */
    /**
     * @return - The session this object was created in.
     */
    /**
     * @param session - The new session this object was created in.
     */
    /**
     * @return - The calendar year this object was active in.
     */
    /**
     * @param year - The new calendar year for this object.
     */
    /**
     * Get the set of SOBI files that contain modifications for this object.
     */
    /**
     * @param dataSources - The new set of data sources that contain modifications for this object.
     */

    /**
     * Add a new source to the set of data sources. This is preferred to
     * getting a reference to the list and directly adding filenames
     *
     * @param source - The new source to add to the data sources.
     */
    public void addDataSource(String source)
    {
        dataSources.add(source);
    }
src/main/java/gov/nysenate/openleg/model/BaseObject.java
/**
     * Set the last publish date.
     *
     * @param publishDate - The new publish date.
     */
    public void setPublishDate(Date publishDate)
    {
    /**
     * @return - true if the object has been published
     */
    public boolean isPublished()
    {
    /**
     * Gets the last modified time in milliseconds since epoch.
     */
    /**
     * Sets the last modified timestamp in milliseconds since epoch.
     */
    /**
     * @return - The session this object was created in.
     */
    /**
     * @param session - The new session this object was created in.
     */
    /**
     * @return - The calendar year this object was active in.
     */
    /**
     * @param year - The new calendar year for this object.
     */
    /**
     * Get the set of SOBI files that contain modifications for this object.
     */
    /**
     * @param dataSources - The new set of data sources that contain modifications for this object.
     */

    /**
     * Add a new source to the set of data sources. This is preferred to
     * getting a reference to the list and directly adding filenames
     *
     * @param source - The new source to add to the data sources.
     */
    public void addDataSource(String source)
    {
        dataSources.add(source);
Date getPublishDate()
@@ -5,172 +5,103 @@
 
 /**
  * Implements the BaseObject interface of all published OpenLegislation content.
- *
- * Does NOT implement getOid() or getOType(). All base classes are responsible for forming their
- * own unique object IDs and object types.
- *
  * @author GraylinKim
  */
 abstract public class BaseObject
 {
-    /**
-     * True if the object was not retrieved from storage.
-     */
+    /** True if the object was not retrieved from storage. */
     protected boolean brandNew = true;
 
-    /**
-     * The date the object was most recently modified
-     */
+    /** The date the object was most recently modified. */
     protected Date modifiedDate = null;
 
-    /**
-     * The date the object was most recently published
-     */
+    /** The date the object was most recently published. */
     protected Date publishDate = null;
 
-    /**
-     * The session this object was created in.
-     */
+    /** The session this object was created in. */
     protected int session;
 
-    /**
-     * The calendar year this object was active in.
-     */
+    /** The calendar year this object was active in. */
     protected int year;
 
-    /**
-     * A set of SOBI files that contained modifications to this object.
-     */
+    /** A set of SOBI files that contained modifications to this object. */
     protected HashSet<String> dataSources;
 
-    /**
-     * Initializes BaseObject parameters.
-     */
+    /** Initializes BaseObject parameters. */
     public BaseObject()
     {
         this.dataSources = new HashSet<String>();
     }
 
-    /**
-     * Get the unique id for this object for Lucene/web indexing and purging.
-     */
-    abstract public String getOid();
+    /** --- Functional Getters/Setters --- */
 
     /**
-     * Get the document type for this object.
+     * Add a new source to the set of data sources. This is preferred to
+     * getting a reference to the list and directly adding filenames
+     *
+     * @param source - The new source to add to the data sources.
      */
-    abstract public String getOtype();
+    public void addDataSource(String source) {
+        dataSources.add(source);
+    }
+
+    /** --- Basic Getters/Setters --- */
 
-    /**
-     * @return - True if the object was not retrieved from storage.
-     */
     public boolean isBrandNew() {
         return this.brandNew;
     }
 
-    /**
-     * @param brandNew - The new brandNew state.
-     */
     public void setBrandNew(boolean brandNew) {
         this.brandNew = brandNew;
     }
 
-    /**
-     * @return - The last publish date.
-     */
-    public Date getPublishDate()
-    {
+    public Date getPublishDate() {
         return this.publishDate;
     }
 
-    /**
-     * Set the last publish date.
-     *
-     * @param publishDate - The new publish date.
-     */
-    public void setPublishDate(Date publishDate)
-    {
+    public void setPublishDate(Date publishDate) {
         this.publishDate = publishDate;
     }
 
-    /**
-     * @return - true if the object has been published
-     */
-    public boolean isPublished()
-    {
+    public boolean isPublished() {
         return this.getPublishDate() != null;
     }
 
-    /**
-     * Gets the last modified time in milliseconds since epoch.
-     */
     public Date getModifiedDate()
     {
         return this.modifiedDate;
     }
 
-    /**
-     * Sets the last modified timestamp in milliseconds since epoch.
-     */
     public void setModifiedDate(Date modifiedDate)
     {
         this.modifiedDate = modifiedDate;
     }
 
-    /**
-     * @return - The session this object was created in.
-     */
     public int getSession()
     {
         return this.session;
     }
 
-    /**
-     * @param session - The new session this object was created in.
-     */
     public void setSession(int session)
     {
         this.session = session;
     }
 
-    /**
-     * @return - The calendar year this object was active in.
-     */
     public int getYear() {
         return this.year;
     }
 
-    /**
-     * @param year - The new calendar year for this object.
-     */
     public void setYear(int year) {
         this.year = year;
     }
 
-    /**
-     * Get the set of SOBI files that contain modifications for this object.
-     */
     public HashSet<String> getDataSources()
     {
         return dataSources;
     }
 
-    /**
-     * @param dataSources - The new set of data sources that contain modifications for this object.
-     */
     public void setDataSources(HashSet<String> dataSources)
     {
         this.dataSources = dataSources;
     }
-
-    /**
-     * Add a new source to the set of data sources. This is preferred to
-     * getting a reference to the list and directly adding filenames
-     *
-     * @param source - The new source to add to the data sources.
-     */
-    public void addDataSource(String source)
-    {
-        dataSources.add(source);
-    }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2FBaseObject.java
55f6f91a1995a6091eef758933db7a19fe54da26
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fadmin%2FReportDAO.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fadmin%2FReportDAO.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/admin/ReportDAO.java
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

@@ -1,6 +1,8 @@
 package gov.nysenate.openleg.model.admin;
 
 import gov.nysenate.openleg.util.Application;
+import org.apache.commons.dbutils.QueryRunner;
+import org.apache.commons.dbutils.ResultSetHandler;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -9,9 +11,6 @@
 import java.util.Date;
 import java.util.HashMap;
 
-import org.apache.commons.dbutils.QueryRunner;
-import org.apache.commons.dbutils.ResultSetHandler;
-
 public class ReportDAO
 {
     private static class JoinedReportHandler implements ResultSetHandler<ArrayList<Report>> {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fadmin%2FReportDAO.java
682c644634b3219583f3b517b7e39937a5bf7571
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaInfoAddendum.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaInfoAddendum.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/agenda/AgendaInfoAddendum.java
    @Override
    public String getOid()
    {
        return this.getOtype()+"-"+this.getYear()+"-"+this.getAgendaNumber()+this.getId();
    }

    @Override
    public String getOtype()
    {
        return "agenda-info";
    }

src/main/java/gov/nysenate/openleg/model/agenda/AgendaInfoAddendum.java
return this.getOtype()+"-"+this.getYear()+"-"+this.getAgendaNumber()+this.getId();
String getOid()
src/main/java/gov/nysenate/openleg/model/agenda/AgendaInfoAddendum.java
return "agenda-info";
String getOtype()
@@ -72,18 +72,6 @@ public void removeCommittee(String name)
         this.committees.remove(name);
     }
 
-    @Override
-    public String getOid()
-    {
-        return this.getOtype()+"-"+this.getYear()+"-"+this.getAgendaNumber()+this.getId();
-    }
-
-    @Override
-    public String getOtype()
-    {
-        return "agenda-info";
-    }
-
     public Integer getAgendaNumber()
     {
         return agendaNumber;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaInfoAddendum.java
c33b299989c6924143ae80f96562287cc7756604
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaInfoCommitteeItem.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaInfoCommitteeItem.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/agenda/AgendaInfoCommitteeItem.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.Bill;
@@ -1,6 +1,6 @@
 package gov.nysenate.openleg.model.agenda;
 
-import gov.nysenate.openleg.model.Bill;
+import gov.nysenate.openleg.model.bill.Bill;
 
 public class AgendaInfoCommitteeItem
 {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaInfoCommitteeItem.java
856a58c0cddf0779973feee52fe4b6f470b28587
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteAddendum.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteAddendum.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/agenda/AgendaVoteAddendum.java

    @Override
    public String getOid()
    {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getOtype()
    {
        return "agendavote";
    }
src/main/java/gov/nysenate/openleg/model/agenda/AgendaVoteAddendum.java
// TODO Auto-generated method stub
        return null;
String getOid()
src/main/java/gov/nysenate/openleg/model/agenda/AgendaVoteAddendum.java
return "agendavote";
String getOtype()
@@ -67,17 +67,4 @@ public void setAgendaNumber(Integer id)
     {
         this.agendaNumber = id;
     }
-
-    @Override
-    public String getOid()
-    {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public String getOtype()
-    {
-        return "agendavote";
-    }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteAddendum.java
d3939d0ba98311e64baaa53ba7d04ea745c7aa22
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteCommittee.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteCommittee.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/agenda/AgendaVoteCommittee.java
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -1,10 +1,6 @@
 package gov.nysenate.openleg.model.agenda;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 public class AgendaVoteCommittee
 {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteCommittee.java
ff7f196599fd3f69650c8dcde7ac931dffb200c1
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteCommitteeItem.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteCommitteeItem.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/agenda/AgendaVoteCommitteeItem.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.Bill;
@@ -1,6 +1,6 @@
 package gov.nysenate.openleg.model.agenda;
 
-import gov.nysenate.openleg.model.Bill;
+import gov.nysenate.openleg.model.bill.Bill;
 import gov.nysenate.openleg.processors.SenagendaProcessor;
 
 import java.util.ArrayList;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fagenda%2FAgendaVoteCommitteeItem.java
8bb6d4645c12031664684806454d059e1ebf2cdc
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBill.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBill.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
import gov.nysenate.openleg.model.entity.Person;
import org.codehaus.jackson.annotate.JsonIgnore;
import java.util.*;
/**
 * The Bill class serves as a container for all the entities that can be classified under a print number
 * and session year. It contains a collection of amendments (including the base amendment) as well as
 * shared information such as the sponsor or actions.
 */
    /** The print number of the bill, e.g S1234 */
    protected String printNo = "";
    /** The law code of the bill. */
    protected String law = "";

    /** The summary of the bill. */
    /** Map of amendment version -> Amendment. */
    protected Map<String, BillAmendment> amendmentMap = new TreeMap<>();
    protected String activeVersion = BASE_VERSION;
    protected List<String> previousVersions = new ArrayList<>();
    private List<Person> otherSponsors = new ArrayList<>();
    protected List<String> pastCommittees = new ArrayList<>();
    protected List<BillAction> actions = new ArrayList<>();

    /** --- Constructors --- */
    public Bill(String printNo, int sessionYear) {
        this.printNo = printNo;
        this.session = sessionYear;
        this.year = sessionYear;
    /** --- Functional Getters/Setter --- */

     * Indicates if this bill is currently set to the base version.
    public static boolean isBaseVersion(String version) {
     * Creates a unique id for the bill by using printNo and session year, e.g S1234-2013
    public String getBillId() {
        return this.printNo + "-" + this.session;
     * Indicate if this bill is a resolution by checking that it's not a senate/assembly bill.
     * @return - True if this bill is a resolution of some sort.
    public boolean isResolution() {
        return this.printNo.charAt(0) != 'A' && this.printNo.charAt(0) != 'S';
     * Creates a unique id for the bill with padding to resemble LBDC's representation.
    public String getPaddedBillId() {
        return this.getPaddedPrintNumber() + "-" + this.getSession();
     * Returns the print number padded with 5 zeros, e.g S01234. This is how LBDC represents
     * print numbers in their SOBI files.
    public String getPaddedPrintNumber() {
        Matcher billIdMatcher = printNumberPattern.matcher(this.getPrintNo());
            return String.format("%s%05d%s", billIdMatcher.group(1), Integer.parseInt(billIdMatcher.group(2)),
                                             billIdMatcher.group(3));
        return "";
     * Retrieves an amendment stored in this bill using the version as the key.
     * @return BillAmendment if found, null otherwise
    public BillAmendment getAmendment(String version) {
     * Retrieves a list of all amendments stored in this bill.
     * @return - List<BillAmendment>
    public List<BillAmendment> getAmendmentList() {
     * @param billAmendment - Amendment to add to this bill.
    public void addAmendment(BillAmendment billAmendment) {
    /**
     * Associate a list of amendments with this bill.
     * @param billAmendments - List<Amendment> - Amendments to add to this bill
     */
    public void addAmendments(List<BillAmendment> billAmendments) {
        for (BillAmendment billAmendment : billAmendments) {
            this.addAmendment(billAmendment);
        }
    }

     * Convenience method to retrieve the currently active Amendment object.
     * @return BillAmendment
    public BillAmendment getActiveAmendment() {
     * Add an action to the list of actions.
     * @param action BillAction
    public void addAction(BillAction action) {
        actions.add(action);
     * Adds a committee to the list of past committees.
     * @param committee
    public void addPastCommittee(String committee) {
        if(!pastCommittees.contains(committee)) {
            pastCommittees.add(committee);
    /** --- Delegates --- */
    public String getFulltext() {
    /** --- Basic Getters/Setters --- */
    public String getPrintNo() {
        return printNo;
    public void setPrintNo(String printNo) {
        this.printNo = printNo;
    public String getTitle() {
        return title;
    public void setTitle(String title) {
        this.title = title;
    public String getLawSection() {
        return lawSection;
    public void setLawSection(String lawSection) {
        this.lawSection = lawSection;
    public String getLaw() {
        return law;
    public void setLaw(String law) {
        this.law = law;
    public String getSummary() {
        return summary;
    public void setSummary(String summary) {
        this.summary = summary;
    public String getActiveVersion() {
        return activeVersion;
    public void setActiveVersion(String activeVersion) {
        this.activeVersion = activeVersion;
    public Map<String, BillAmendment> getAmendmentMap() {
        return amendmentMap;
    public List<String> getPreviousVersions() {
        return previousVersions;
    public void setPreviousVersions(List<String> previousVersions) {
        this.previousVersions = previousVersions;
    public List<BillAction> getActions() {
        return actions;
    public void setActions(List<BillAction> actions) {
        this.actions = actions;
    public Person getSponsor() {
        return sponsor;
    public void setSponsor(Person sponsor) {
        this.sponsor = sponsor;
    public List<String> getPastCommittees() {
        return pastCommittees;
    public void setPastCommittees(List<String> pastCommittees) {
        this.pastCommittees = new ArrayList<>(pastCommittees);
    public List<Person> getOtherSponsors() {
        return otherSponsors;
    public void setOtherSponsors(List<Person> otherSponsors) {
        this.otherSponsors = otherSponsors;
    public void setPublishDate(Date publishDate) {
    public Date getModifiedDate() {
    public boolean equals(Object obj) {
    public int compareTo(Bill bill) {
    public static String formatBillNo(String term) {
String printNo  int sessionYear
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.printNo = printNo;
        this.session = sessionYear;
        this.year = sessionYear;
    /** --- Functional Getters/Setter --- */

     * Indicates if this bill is currently set to the base version.
    public static boolean isBaseVersion(String version) {
     * Creates a unique id for the bill by using printNo and session year, e.g S1234-2013
    public String getBillId() {
        return this.printNo + "-" + this.session;
     * Indicate if this bill is a resolution by checking that it's not a senate/assembly bill.
     * @return - True if this bill is a resolution of some sort.
    public boolean isResolution() {
        return this.printNo.charAt(0) != 'A' && this.printNo.charAt(0) != 'S';
     * Creates a unique id for the bill with padding to resemble LBDC's representation.
    public String getPaddedBillId() {
        return this.getPaddedPrintNumber() + "-" + this.getSession();
     * Returns the print number padded with 5 zeros, e.g S01234. This is how LBDC represents
     * print numbers in their SOBI files.
    public String getPaddedPrintNumber() {
        Matcher billIdMatcher = printNumberPattern.matcher(this.getPrintNo());
            return String.format("%s%05d%s", billIdMatcher.group(1), Integer.parseInt(billIdMatcher.group(2)),
                                             billIdMatcher.group(3));
        return "";
     * Retrieves an amendment stored in this bill using the version as the key.
     * @return BillAmendment if found, null otherwise
    public BillAmendment getAmendment(String version) {
     * Retrieves a list of all amendments stored in this bill.
     * @return - List<BillAmendment>
    public List<BillAmendment> getAmendmentList() {
     * @param billAmendment - Amendment to add to this bill.
    public void addAmendment(BillAmendment billAmendment) {
    /**
     * Associate a list of amendments with this bill.
     * @param billAmendments - List<Amendment> - Amendments to add to this bill
     */
    public void addAmendments(List<BillAmendment> billAmendments) {
        for (BillAmendment billAmendment : billAmendments) {
            this.addAmendment(billAmendment);
public Bill(String printNo, int sessionYear)
import gov.nysenate.openleg.model.Person;
import gov.nysenate.openleg.model.Vote;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.annotate.JsonIgnore;

    public static Pattern billIdPattern = Pattern.compile("("+printNumberPattern.pattern()+")-([0-9]{4})");
    /** Error message for when an invalid bill version is supplied as an argument */
    private static final String INVALID_AMENDMENT_VERSION = "Specified amendment version does not exist: ";

    /** The unique bill id. */
    protected String billId = "";
    /** The summary of the amendment. */
    /** Map of amendment version -> Amendment. The default amendment should
     *  not be included in this mapping. */
    protected LinkedHashMap<String, BillAmendment> amendmentMap = new LinkedHashMap<>();
    protected String activeVersion = Bill.BASE_VERSION;

    /** Retains a history of versions that were activated */
    protected LinkedList<String> activeHistory = new LinkedList<>();
    protected List<String> previousVersions = new ArrayList<String>();
    private List<Person> otherSponsors = new ArrayList<Person>();
    protected List<String> pastCommittees = new ArrayList<String>();
    protected List<BillAction> actions = new ArrayList<BillAction>();
    /**
     * Constructs a minimal Bill object.
     *
     * @param billId - The unique bill id
     * @param session - The session year this bill was introduced to
     */
    public Bill(String billId, int session)
    {
        this();
        this.setBillId(billId);
        this.setSession(session);
        this.setYear(session);
    @JsonIgnore
    public static boolean isBaseVersion(String version)
    {
     * @return - True if this bill is a resolution of some sort.
     */
    @JsonIgnore
    public boolean isResolution()
    {
        return billId.charAt(0)!='A' && billId.charAt(0)!='S';
    }

    /**
     * @return - The URL used to construct Disqus threads
     */
    @JsonIgnore
    public String getDisqusUrl()
    {
        if (this.getSession()==2009) {
            String disqusId = this.getBillId().split("-")[0];
            return "http://open.nysenate.gov/legislation/api/html/bill/" + disqusId;
        }
        else {
            String disqusId = this.getBillId();
            return "http://open.nysenate.gov/legislation/bill/" + disqusId;
        }
    }

    /**
    public String getBillId()
    {
        return billId;
     * @param billId - The new bill id.
     */
    public void setBillId(String billId)
    {
        this.billId = billId;
    }

    /**
     * The object type of the bill.
     */
    @JsonIgnore
    public String getOtype()
    {
        return "bill";
    }

    /**
     * @return - The unique object id.
    @JsonIgnore
    public String getOid()
    {
        return this.getBillId();
    @JsonIgnore
    public String getPaddedBillId()
    {
        return this.getPaddedPrintNumber()+"-"+this.getSession();
    @JsonIgnore
    public String getPaddedPrintNumber()
    {
        Matcher billIdMatcher = billIdPattern.matcher(this.getBillId());
        if (billIdMatcher.find()) {
            return String.format("%s%05d%s", billIdMatcher.group(2), Integer.parseInt(billIdMatcher.group(3)), billIdMatcher.group(4));
        }
        else {
            // logger.warn("Invalid senateBillNo: "+this.senateBillNo);
            return "";
        }
    }

    /**
     * @return - Just the print number without the session year suffix.
     */
    @JsonIgnore
    public String getPrintNumber()
    {
        Matcher billIdMatcher = billIdPattern.matcher(this.getBillId());
            return billIdMatcher.group(1);
        }
        else {
            // logger.warn("Invalid senateBillNo: "+this.senateBillNo);
            return "";
     * @return Amendment if found, null otherwise
    @JsonIgnore
    public BillAmendment getAmendment(String version)
    {
     * @return - A list of all Amendments associated with this Bill.
    @JsonIgnore
    public List<BillAmendment> getAmendmentList()
    {
     * @param billAmendment - Amendment to associate.
    public void addAmendment(BillAmendment billAmendment)
    {
     * @return Mapping of Amendment versions to Amendment objects for keyed retrieval.
    public Map<String, BillAmendment> getAmendmentMap()
    {
        return amendmentMap;
    }

    public BillAmendment getActiveAmendment()
    {
    /**
     * @return The active amendment version.
     */
    public String getActiveVersion()
    {
        return activeVersion;
    }

    /**
     * @param activeVersion - The new active amendment version.
     */
    public void setActiveVersion(String activeVersion)
    {
        this.activeVersion = activeVersion;
    }

    public LinkedList<String> getActiveHistory() {
        return activeHistory;
    }

    public void setActiveHistory(LinkedList<String> activeHistory) {
        this.activeHistory = activeHistory;
    }

    /**
     * @return - The current bill title
     */
    public String getTitle()
    {
        return title;
    }

    /**
     * @param title - The new bill title
     */
    public void setTitle(String title)
    {
        this.title = title;
    }

    /**
     * @return - The current law section.
     */
    public String getLawSection()
    {
        return lawSection;
    }

    public void setLawSection(String lawSection)
    {
        this.lawSection = lawSection;
    }

    public String getSameAs()
    {
        return this.getActiveAmendment().getSameAs();
    }

    public String getSameAs(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getSameAs();
        }
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    }

    /**
     * @param sameAs - The new same as bill ID.
     */
    public void setSameAs(String sameAs, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setSameAs(sameAs);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    }

    /**
     * @return - The current list of bill ids of previous bill versions.
     */
    public List<String> getPreviousVersions()
    {
        return previousVersions;
    }

    /**
     * @param previousVersions - The new list of bill IDs of previous versions.
     */
    public void setPreviousVersions(List<String> previousVersions)
    {
        this.previousVersions = previousVersions;
    }

     * @return - The current bill sponsor.
     */
    public Person getSponsor()
    {
        return sponsor;
    }

    /**
     * @param sponsor - The new bill sponsor.
     */
    public void setSponsor(Person sponsor)
    {
        this.sponsor = sponsor;
    }

    /**
     * @return - The current list of "other" sponsors.
     */
    public List<Person> getOtherSponsors()
    {
        return otherSponsors;
    }

    /**
     * @param otherSponsors - The new list of "other" sponsors.
    public void setOtherSponsors(List<Person> otherSponsors)
    {
        this.otherSponsors = otherSponsors;
    }

    public List<Person> getCoSponsors()
    {
        return this.getActiveAmendment().getCoSponsors();
    }

    public List<Person> getCoSponsors(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getCoSponsors();
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
     * @param coSponsors - The new list of cosponsors.
    public void setCoSponsors(List<Person> coSponsors, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setCoSponsors(coSponsors);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    }

    public List<Person> getMultiSponsors()
    {
        return this.getActiveAmendment().getMultiSponsors();
    }

    public List<Person> getMultiSponsorsList(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getMultiSponsors();
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    public void setMultiSponsors(List<Person> multiSponsors, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setMultiSponsors(multiSponsors);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    }
    public String getSummary()
    {
        return summary;
    }

    public void setSummary(String summary)
    {
        this.summary = summary;
    }

    public List<BillAction> getActions()
    {
        return actions;
    }

    public void setActions(List<BillAction> actions)
    {
        this.actions = actions;
    }

    public void addAction(BillAction action) {
        actions.add(action);
    }

    public String getFulltext()
    {
    public String getFulltext(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getFulltext();
        }
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    }
    public void setFulltext(String fulltext, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setFulltext(fulltext);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    public String getMemo()
    {
        return this.getActiveAmendment().getMemo();
    public String getMemo(String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).getMemo();
        }
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    public void setMemo(String memo, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setMemo(memo);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    public String getLaw()
    {
        return this.getActiveAmendment().getLaw();
    public String getLaw(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getLaw();
        }
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    public void setLaw(String law, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setLaw(law);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    public String getActClause(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getActClause();
        }
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    public void setActClause(String actClause, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setActClause(actClause);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    public Boolean isStricken(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).isStricken();
        }
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    public void setStricken(Boolean stricken, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setStricken(stricken);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    public boolean isUniBill(String version)
    {
        if (hasAmendment(version)) {
            return this.getAmendment(version).isUniBill();
        }
        else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    public void setUniBill(boolean uniBill, String version)
    {
        if (hasAmendment(version)) {
            this.getAmendment(version).setUniBill(uniBill);
        }
        else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }

    public String getCurrentCommittee()
    {
        return this.getActiveAmendment().getCurrentCommittee();
    public String getCurrentCommittee(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getCurrentCommittee();
        }
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    public void setCurrentCommittee(String currentCommittee, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setCurrentCommittee(currentCommittee);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    public List<String> getPastCommittees()
    {
        return pastCommittees;
    public void setPastCommittees(List<String> pastCommittees)
    {
        this.pastCommittees = new ArrayList<String>(pastCommittees);
    public void addPastCommittee(String committee)
    {
        if(!pastCommittees.contains(committee)) {
            pastCommittees.add(committee);
        }
    public List<Vote> getVotes()
    {
        ArrayList<Vote> votes = new ArrayList<Vote>();
        for(BillAmendment amendment : this.getAmendmentList()) {
            votes.addAll(amendment.getVotes());
        }
        return votes;
    public List<Vote> getVotes(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getVotes();
        }
        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
    public void setVotes(List<Vote> votes, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).setVotes(votes);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    /**
     * @param vote - The new vote to add to the list of votes on this bill. Replaces exiting vote data if a match is found.
     */
    public void updateVote(Vote vote, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).getVotes().remove(vote);
            getAmendment(version).getVotes().add(vote);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    public void setPublishDate(Date publishDate)
    {
    public Date getModifiedDate()
    {
    /**
     * @param vote - The vote to remove from the list of votes on this bill.
     */
    public void removeVote(Vote vote, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).getVotes().remove(vote);
        } else {
            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
        }
    }

    public boolean equals(Object obj)
    {
    public int compareTo(Bill bill)
    {
    public static String formatBillNo(String term)
    {
String billId  int session
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this();
        this.setBillId(billId);
        this.setSession(session);
        this.setYear(session);
    @JsonIgnore
    public static boolean isBaseVersion(String version)
    {
     * @return - True if this bill is a resolution of some sort.
     */
    @JsonIgnore
    public boolean isResolution()
    {
        return billId.charAt(0)!='A' && billId.charAt(0)!='S';
public Bill(String billId, int session)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (this.getSession()==2009) {
            String disqusId = this.getBillId().split("-")[0];
            return "http://open.nysenate.gov/legislation/api/html/bill/" + disqusId;
String getDisqusUrl()
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return billId;
     * @param billId - The new bill id.
     */
    public void setBillId(String billId)
    {
        this.billId = billId;
String getBillId()
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return "bill";
String getOtype()
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return this.getBillId();
    @JsonIgnore
    public String getPaddedBillId()
    {
        return this.getPaddedPrintNumber()+"-"+this.getSession();
    @JsonIgnore
    public String getPaddedPrintNumber()
    {
        Matcher billIdMatcher = billIdPattern.matcher(this.getBillId());
        if (billIdMatcher.find()) {
            return String.format("%s%05d%s", billIdMatcher.group(2), Integer.parseInt(billIdMatcher.group(3)), billIdMatcher.group(4));
String getOid()
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
Matcher billIdMatcher = billIdPattern.matcher(this.getBillId());
            return billIdMatcher.group(1);
String getPrintNumber()
String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
* @return - A list of all Amendments associated with this Bill.
    @JsonIgnore
    public List<BillAmendment> getAmendmentList()
    {
     * @param billAmendment - Amendment to associate.
    public void addAmendment(BillAmendment billAmendment)
    {
     * @return Mapping of Amendment versions to Amendment objects for keyed retrieval.
    public Map<String, BillAmendment> getAmendmentMap()
    {
        return amendmentMap;
BillAmendment getAmendment(String version)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
/**
     * @return The active amendment version.
     */
    public String getActiveVersion()
    {
        return activeVersion;
BillAmendment getActiveAmendment()
String activeVersion
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.activeVersion = activeVersion;
void setActiveVersion(String activeVersion)
LinkedList<String> activeHistory
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.activeHistory = activeHistory;
void setActiveHistory(LinkedList<String> activeHistory)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return title;
String getTitle()
String title
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.title = title;
void setTitle(String title)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return lawSection;
String getLawSection()
String lawSection
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.lawSection = lawSection;
void setLawSection(String lawSection)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return this.getActiveAmendment().getSameAs();
String getSameAs()
String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            return getAmendment(version).getSameAs();
String getSameAs(String version)
String sameAs  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setSameAs(sameAs);
void setSameAs(String sameAs, String version)
List<String> previousVersions
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.previousVersions = previousVersions;
void setPreviousVersions(List<String> previousVersions)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return sponsor;
Person getSponsor()
Person sponsor
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.sponsor = sponsor;
void setSponsor(Person sponsor)
List<Person> otherSponsors
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.otherSponsors = otherSponsors;
void setOtherSponsors(List<Person> otherSponsors)
List<Person> coSponsors  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setCoSponsors(coSponsors);
void setCoSponsors(List<Person> coSponsors, String version)
List<Person> multiSponsors  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setMultiSponsors(multiSponsors);
void setMultiSponsors(List<Person> multiSponsors, String version)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return summary;
String getSummary()
String summary
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.summary = summary;
void setSummary(String summary)
List<BillAction> actions
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.actions = actions;
void setActions(List<BillAction> actions)
BillAction action
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
actions.add(action);
void addAction(BillAction action)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
public String getFulltext(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getFulltext();
String getFulltext()
String fulltext  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setFulltext(fulltext);
void setFulltext(String fulltext, String version)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return this.getActiveAmendment().getMemo();
    public String getMemo(String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).getMemo();
String getMemo()
String memo  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setMemo(memo);
void setMemo(String memo, String version)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return this.getActiveAmendment().getLaw();
    public String getLaw(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getLaw();
String getLaw()
String law  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setLaw(law);
void setLaw(String law, String version)
String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            return getAmendment(version).getActClause();
String getActClause(String version)
String actClause  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setActClause(actClause);
void setActClause(String actClause, String version)
String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            return getAmendment(version).isStricken();
Boolean isStricken(String version)
Boolean stricken  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setStricken(stricken);
void setStricken(Boolean stricken, String version)
String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            return this.getAmendment(version).isUniBill();
boolean isUniBill(String version)
boolean uniBill  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            this.getAmendment(version).setUniBill(uniBill);
void setUniBill(boolean uniBill, String version)
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
return this.getActiveAmendment().getCurrentCommittee();
    public String getCurrentCommittee(String version)
    {
        if (hasAmendment(version)) {
            return getAmendment(version).getCurrentCommittee();
String getCurrentCommittee()
String currentCommittee  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setCurrentCommittee(currentCommittee);
void setCurrentCommittee(String currentCommittee, String version)
List<String> pastCommittees
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
this.pastCommittees = new ArrayList<String>(pastCommittees);
    public void addPastCommittee(String committee)
    {
        if(!pastCommittees.contains(committee)) {
            pastCommittees.add(committee);
void setPastCommittees(List<String> pastCommittees)
List<Vote> votes  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).setVotes(votes);
void setVotes(List<Vote> votes, String version)
Vote vote  String version
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
if (hasAmendment(version)) {
            getAmendment(version).getVotes().remove(vote);
            getAmendment(version).getVotes().add(vote);
void updateVote(Vote vote, String version)
Date publishDate
src/main/java/gov/nysenate/openleg/model/bill/Bill.java
public Date getModifiedDate()
    {
    /**
     * @param vote - The vote to remove from the list of votes on this bill.
     */
    public void removeVote(Vote vote, String version)
    {
        if (hasAmendment(version)) {
            getAmendment(version).getVotes().remove(vote);
void setPublishDate(Date publishDate)
@@ -1,226 +1,159 @@
 package gov.nysenate.openleg.model.bill;
 
 import gov.nysenate.openleg.model.BaseObject;
-import gov.nysenate.openleg.model.Person;
-import gov.nysenate.openleg.model.Vote;
+import gov.nysenate.openleg.model.entity.Person;
 import gov.nysenate.openleg.util.SessionYear;
 import gov.nysenate.openleg.util.TextFormatter;
+import org.codehaus.jackson.annotate.JsonIgnore;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.codehaus.jackson.annotate.JsonIgnore;
-
+/**
+ * The Bill class serves as a container for all the entities that can be classified under a print number
+ * and session year. It contains a collection of amendments (including the base amendment) as well as
+ * shared information such as the sponsor or actions.
+ */
 public class Bill extends BaseObject implements Comparable<Bill>
 {
     public static Pattern printNumberPattern = Pattern.compile("([ASLREJKBC])([0-9]{1,5})([A-Z]?)");
-    public static Pattern billIdPattern = Pattern.compile("("+printNumberPattern.pattern()+")-([0-9]{4})");
 
     /** The default amendment version letter. */
     public static final String BASE_VERSION = "";
 
-    /** Error message for when an invalid bill version is supplied as an argument */
-    private static final String INVALID_AMENDMENT_VERSION = "Specified amendment version does not exist: ";
-
-    /** The unique bill id. */
-    protected String billId = "";
+    /** The print number of the bill, e.g S1234 */
+    protected String printNo = "";
 
     /** The bill title. */
     protected String title = "";
 
     /** The section of the law the bill affects. */
     protected String lawSection = "";
 
-    /** The summary of the amendment. */
+    /** The law code of the bill. */
+    protected String law = "";
+
+    /** The summary of the bill. */
     protected String summary = "";
 
-    /** Map of amendment version -> Amendment. The default amendment should
-     *  not be included in this mapping. */
-    protected LinkedHashMap<String, BillAmendment> amendmentMap = new LinkedHashMap<>();
+    /** Map of amendment version -> Amendment. */
+    protected Map<String, BillAmendment> amendmentMap = new TreeMap<>();
 
     /** Indicates the amendment version that is active for this bill. */
-    protected String activeVersion = Bill.BASE_VERSION;
-
-    /** Retains a history of versions that were activated */
-    protected LinkedList<String> activeHistory = new LinkedList<>();
+    protected String activeVersion = BASE_VERSION;
 
     /** A list of ids of versions of this legislation in previous sessions. */
-    protected List<String> previousVersions = new ArrayList<String>();
+    protected List<String> previousVersions = new ArrayList<>();
 
     /** The sponsor of this bill. */
     protected Person sponsor;
 
     /** A list of coSponsors to be given preferential display treatment. */
-    private List<Person> otherSponsors = new ArrayList<Person>();
+    private List<Person> otherSponsors = new ArrayList<>();
 
     /** A list of committees this bill has been referred to. */
-    protected List<String> pastCommittees = new ArrayList<String>();
+    protected List<String> pastCommittees = new ArrayList<>();
 
     /** A list of actions that have been made on this bill. */
-    protected List<BillAction> actions = new ArrayList<BillAction>();
+    protected List<BillAction> actions = new ArrayList<>();
+
+    /** --- Constructors --- */
 
     public Bill() {
         super();
     }
 
-    /**
-     * Constructs a minimal Bill object.
-     *
-     * @param billId - The unique bill id
-     * @param session - The session year this bill was introduced to
-     */
-    public Bill(String billId, int session)
-    {
-        this();
-        this.setBillId(billId);
-        this.setSession(session);
-        this.setYear(session);
+    public Bill(String printNo, int sessionYear) {
+        this.printNo = printNo;
+        this.session = sessionYear;
+        this.year = sessionYear;
     }
 
+    /** --- Functional Getters/Setter --- */
+
     /**
+     * Indicates if this bill is currently set to the base version.
      * @param version The bill version
      * @return Returns true if the version will be represented as a base bill
      */
-    @JsonIgnore
-    public static boolean isBaseVersion(String version)
-    {
+    public static boolean isBaseVersion(String version) {
         return version == null || version.equals(BASE_VERSION);
     }
 
     /**
-     * @return - True if this bill is a resolution of some sort.
-     */
-    @JsonIgnore
-    public boolean isResolution()
-    {
-        return billId.charAt(0)!='A' && billId.charAt(0)!='S';
-    }
-
-    /**
-     * @return - The URL used to construct Disqus threads
-     */
-    @JsonIgnore
-    public String getDisqusUrl()
-    {
-        if (this.getSession()==2009) {
-            String disqusId = this.getBillId().split("-")[0];
-            return "http://open.nysenate.gov/legislation/api/html/bill/" + disqusId;
-        }
-        else {
-            String disqusId = this.getBillId();
-            return "http://open.nysenate.gov/legislation/bill/" + disqusId;
-        }
-    }
-
-    /**
+     * Creates a unique id for the bill by using printNo and session year, e.g S1234-2013
      * @return - The bill's unique id.
      */
-    public String getBillId()
-    {
-        return billId;
+    public String getBillId() {
+        return this.printNo + "-" + this.session;
     }
 
     /**
-     * @param billId - The new bill id.
-     */
-    public void setBillId(String billId)
-    {
-        this.billId = billId;
-    }
-
-    /**
-     * The object type of the bill.
-     */
-    @JsonIgnore
-    public String getOtype()
-    {
-        return "bill";
-    }
-
-    /**
-     * @return - The unique object id.
+     * Indicate if this bill is a resolution by checking that it's not a senate/assembly bill.
+     * @return - True if this bill is a resolution of some sort.
      */
-    @JsonIgnore
-    public String getOid()
-    {
-        return this.getBillId();
+    public boolean isResolution() {
+        return this.printNo.charAt(0) != 'A' && this.printNo.charAt(0) != 'S';
     }
 
     /**
+     * Creates a unique id for the bill with padding to resemble LBDC's representation.
      * @return - The billId padded to 5 digits with zeros.
      */
-    @JsonIgnore
-    public String getPaddedBillId()
-    {
-        return this.getPaddedPrintNumber()+"-"+this.getSession();
+    public String getPaddedBillId() {
+        return this.getPaddedPrintNumber() + "-" + this.getSession();
     }
 
     /**
+     * Returns the print number padded with 5 zeros, e.g S01234. This is how LBDC represents
+     * print numbers in their SOBI files.
      * @return - The print number padded to 5 digits with zeros.
      */
-    @JsonIgnore
-    public String getPaddedPrintNumber()
-    {
-        Matcher billIdMatcher = billIdPattern.matcher(this.getBillId());
-        if (billIdMatcher.find()) {
-            return String.format("%s%05d%s", billIdMatcher.group(2), Integer.parseInt(billIdMatcher.group(3)), billIdMatcher.group(4));
-        }
-        else {
-            // logger.warn("Invalid senateBillNo: "+this.senateBillNo);
-            return "";
-        }
-    }
-
-    /**
-     * @return - Just the print number without the session year suffix.
-     */
-    @JsonIgnore
-    public String getPrintNumber()
-    {
-        Matcher billIdMatcher = billIdPattern.matcher(this.getBillId());
+    public String getPaddedPrintNumber() {
+        Matcher billIdMatcher = printNumberPattern.matcher(this.getPrintNo());
         if (billIdMatcher.find()) {
-            return billIdMatcher.group(1);
-        }
-        else {
-            // logger.warn("Invalid senateBillNo: "+this.senateBillNo);
-            return "";
+            return String.format("%s%05d%s", billIdMatcher.group(1), Integer.parseInt(billIdMatcher.group(2)),
+                                             billIdMatcher.group(3));
         }
+        return "";
     }
 
     /**
+     * Retrieves an amendment stored in this bill using the version as the key.
      * @param version - The amendment version of the bill (e.g "A", "B", etc)
-     * @return Amendment if found, null otherwise
+     * @return BillAmendment if found, null otherwise
      */
-    @JsonIgnore
-    public BillAmendment getAmendment(String version)
-    {
+    public BillAmendment getAmendment(String version) {
         return this.amendmentMap.get(version.toUpperCase());
     }
 
     /**
-     * @return - A list of all Amendments associated with this Bill.
+     * Retrieves a list of all amendments stored in this bill.
+     * @return - List<BillAmendment>
      */
-    @JsonIgnore
-    public List<BillAmendment> getAmendmentList()
-    {
+    public List<BillAmendment> getAmendmentList() {
         return new ArrayList<>(this.amendmentMap.values());
     }
 
     /**
      * Associate an amendment with this bill.
-     * @param billAmendment - Amendment to associate.
+     * @param billAmendment - Amendment to add to this bill.
      */
-    public void addAmendment(BillAmendment billAmendment)
-    {
+    public void addAmendment(BillAmendment billAmendment) {
         this.amendmentMap.put(billAmendment.getVersion().toUpperCase(), billAmendment);
     }
 
+    /**
+     * Associate a list of amendments with this bill.
+     * @param billAmendments - List<Amendment> - Amendments to add to this bill
+     */
+    public void addAmendments(List<BillAmendment> billAmendments) {
+        for (BillAmendment billAmendment : billAmendments) {
+            this.addAmendment(billAmendment);
+        }
+    }
+
     /**
      * Indicate whether the bill has a reference to a given amendment version.
      * @param version String - Amendment version
@@ -233,112 +166,13 @@ public boolean hasAmendment(String version)
     }
 
     /**
-     * @return Mapping of Amendment versions to Amendment objects for keyed retrieval.
+     * Convenience method to retrieve the currently active Amendment object.
+     * @return BillAmendment
      */
-    public Map<String, BillAmendment> getAmendmentMap()
-    {
-        return amendmentMap;
-    }
-
-    public BillAmendment getActiveAmendment()
-    {
+    public BillAmendment getActiveAmendment() {
         return this.getAmendment(this.getActiveVersion());
     }
 
-    /**
-     * @return The active amendment version.
-     */
-    public String getActiveVersion()
-    {
-        return activeVersion;
-    }
-
-    /**
-     * @param activeVersion - The new active amendment version.
-     */
-    public void setActiveVersion(String activeVersion)
-    {
-        this.activeVersion = activeVersion;
-    }
-
-    public LinkedList<String> getActiveHistory() {
-        return activeHistory;
-    }
-
-    public void setActiveHistory(LinkedList<String> activeHistory) {
-        this.activeHistory = activeHistory;
-    }
-
-    /**
-     * @return - The current bill title
-     */
-    public String getTitle()
-    {
-        return title;
-    }
-
-    /**
-     * @param title - The new bill title
-     */
-    public void setTitle(String title)
-    {
-        this.title = title;
-    }
-
-    /**
-     * @return - The current law section.
-     */
-    public String getLawSection()
-    {
-        return lawSection;
-    }
-
-    public void setLawSection(String lawSection)
-    {
-        this.lawSection = lawSection;
-    }
-
-    public String getSameAs()
-    {
-        return this.getActiveAmendment().getSameAs();
-    }
-
-    public String getSameAs(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).getSameAs();
-        }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-    }
-
-    /**
-     * @param sameAs - The new same as bill ID.
-     */
-    public void setSameAs(String sameAs, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setSameAs(sameAs);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
-    }
-
-    /**
-     * @return - The current list of bill ids of previous bill versions.
-     */
-    public List<String> getPreviousVersions()
-    {
-        return previousVersions;
-    }
-
-    /**
-     * @param previousVersions - The new list of bill IDs of previous versions.
-     */
-    public void setPreviousVersions(List<String> previousVersions)
-    {
-        this.previousVersions = previousVersions;
-    }
-
     /**
      * @param previousVersion - The new bill ID to add to the previous versions list.
      */
@@ -349,306 +183,121 @@ public void addPreviousVersion(String previousVersion) {
     }
 
     /**
-     * @return - The current bill sponsor.
-     */
-    public Person getSponsor()
-    {
-        return sponsor;
-    }
-
-    /**
-     * @param sponsor - The new bill sponsor.
-     */
-    public void setSponsor(Person sponsor)
-    {
-        this.sponsor = sponsor;
-    }
-
-    /**
-     * @return - The current list of "other" sponsors.
-     */
-    public List<Person> getOtherSponsors()
-    {
-        return otherSponsors;
-    }
-
-    /**
-     * @param otherSponsors - The new list of "other" sponsors.
+     * Add an action to the list of actions.
+     * @param action BillAction
      */
-    public void setOtherSponsors(List<Person> otherSponsors)
-    {
-        this.otherSponsors = otherSponsors;
-    }
-
-    public List<Person> getCoSponsors()
-    {
-        return this.getActiveAmendment().getCoSponsors();
-    }
-
-    public List<Person> getCoSponsors(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).getCoSponsors();
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public void addAction(BillAction action) {
+        actions.add(action);
     }
 
     /**
-     * @param coSponsors - The new list of cosponsors.
+     * Adds a committee to the list of past committees.
+     * @param committee
      */
-    public void setCoSponsors(List<Person> coSponsors, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setCoSponsors(coSponsors);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
-    }
-
-    public List<Person> getMultiSponsors()
-    {
-        return this.getActiveAmendment().getMultiSponsors();
-    }
-
-    public List<Person> getMultiSponsorsList(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).getMultiSponsors();
+    public void addPastCommittee(String committee) {
+        if(!pastCommittees.contains(committee)) {
+            pastCommittees.add(committee);
         }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
     }
 
-    public void setMultiSponsors(List<Person> multiSponsors, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setMultiSponsors(multiSponsors);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
-    }
+    /** --- Delegates --- */
 
-    public String getSummary()
-    {
-        return summary;
-    }
-
-    public void setSummary(String summary)
-    {
-        this.summary = summary;
-    }
-
-    public List<BillAction> getActions()
-    {
-        return actions;
-    }
-
-    public void setActions(List<BillAction> actions)
-    {
-        this.actions = actions;
-    }
-
-    public void addAction(BillAction action) {
-        actions.add(action);
-    }
-
-    public String getFulltext()
-    {
+    public String getFulltext() {
         return this.getActiveAmendment().getFulltext();
     }
 
-    public String getFulltext(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).getFulltext();
-        }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-    }
+    /** --- Basic Getters/Setters --- */
 
-    public void setFulltext(String fulltext, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setFulltext(fulltext);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public String getPrintNo() {
+        return printNo;
     }
 
-    public String getMemo()
-    {
-        return this.getActiveAmendment().getMemo();
+    public void setPrintNo(String printNo) {
+        this.printNo = printNo;
     }
 
-    public String getMemo(String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).getMemo();
-        }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
+    public String getTitle() {
+        return title;
     }
 
-    public void setMemo(String memo, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setMemo(memo);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public void setTitle(String title) {
+        this.title = title;
     }
 
-    public String getLaw()
-    {
-        return this.getActiveAmendment().getLaw();
+    public String getLawSection() {
+        return lawSection;
     }
 
-    public String getLaw(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).getLaw();
-        }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
+    public void setLawSection(String lawSection) {
+        this.lawSection = lawSection;
     }
 
-    public void setLaw(String law, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setLaw(law);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public String getLaw() {
+        return law;
     }
 
-    public String getActClause(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).getActClause();
-        }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
+    public void setLaw(String law) {
+        this.law = law;
     }
 
-    public void setActClause(String actClause, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setActClause(actClause);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public String getSummary() {
+        return summary;
     }
 
-    public Boolean isStricken(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).isStricken();
-        }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
+    public void setSummary(String summary) {
+        this.summary = summary;
     }
 
-    public void setStricken(Boolean stricken, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setStricken(stricken);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public String getActiveVersion() {
+        return activeVersion;
     }
 
-    public boolean isUniBill(String version)
-    {
-        if (hasAmendment(version)) {
-            return this.getAmendment(version).isUniBill();
-        }
-        else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public void setActiveVersion(String activeVersion) {
+        this.activeVersion = activeVersion;
     }
 
-    public void setUniBill(boolean uniBill, String version)
-    {
-        if (hasAmendment(version)) {
-            this.getAmendment(version).setUniBill(uniBill);
-        }
-        else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
-
+    public Map<String, BillAmendment> getAmendmentMap() {
+        return amendmentMap;
     }
 
-    public String getCurrentCommittee()
-    {
-        return this.getActiveAmendment().getCurrentCommittee();
+    public List<String> getPreviousVersions() {
+        return previousVersions;
     }
 
-    public String getCurrentCommittee(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).getCurrentCommittee();
-        }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
+    public void setPreviousVersions(List<String> previousVersions) {
+        this.previousVersions = previousVersions;
     }
 
-    public void setCurrentCommittee(String currentCommittee, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setCurrentCommittee(currentCommittee);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public List<BillAction> getActions() {
+        return actions;
     }
 
-    public List<String> getPastCommittees()
-    {
-        return pastCommittees;
+    public void setActions(List<BillAction> actions) {
+        this.actions = actions;
     }
 
-    public void setPastCommittees(List<String> pastCommittees)
-    {
-        this.pastCommittees = new ArrayList<String>(pastCommittees);
+    public Person getSponsor() {
+        return sponsor;
     }
 
-    public void addPastCommittee(String committee)
-    {
-        if(!pastCommittees.contains(committee)) {
-            pastCommittees.add(committee);
-        }
+    public void setSponsor(Person sponsor) {
+        this.sponsor = sponsor;
     }
 
-    public List<Vote> getVotes()
-    {
-        ArrayList<Vote> votes = new ArrayList<Vote>();
-        for(BillAmendment amendment : this.getAmendmentList()) {
-            votes.addAll(amendment.getVotes());
-        }
-        return votes;
+    public List<String> getPastCommittees() {
+        return pastCommittees;
     }
 
-    public List<Vote> getVotes(String version)
-    {
-        if (hasAmendment(version)) {
-            return getAmendment(version).getVotes();
-        }
-        throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
+    public void setPastCommittees(List<String> pastCommittees) {
+        this.pastCommittees = new ArrayList<>(pastCommittees);
     }
 
-    public void setVotes(List<Vote> votes, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).setVotes(votes);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public List<Person> getOtherSponsors() {
+        return otherSponsors;
     }
 
-    /**
-     * @param vote - The new vote to add to the list of votes on this bill. Replaces exiting vote data if a match is found.
-     */
-    public void updateVote(Vote vote, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).getVotes().remove(vote);
-            getAmendment(version).getVotes().add(vote);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
+    public void setOtherSponsors(List<Person> otherSponsors) {
+        this.otherSponsors = otherSponsors;
     }
 
     @Override
@@ -664,15 +313,13 @@ public Date getPublishDate()
     }
 
     @Override
-    public void setPublishDate(Date publishDate)
-    {
+    public void setPublishDate(Date publishDate) {
         throw new RuntimeException("Cannot set publish on the bill container.");
     }
 
     @Override
     @JsonIgnore
-    public Date getModifiedDate()
-    {
+    public Date getModifiedDate() {
         for (BillAmendment amendment : amendmentMap.values()) {
             if (amendment.getModifiedDate().after(this.modifiedDate)) {
                 return amendment.getModifiedDate();
@@ -681,21 +328,8 @@ public Date getModifiedDate()
         return this.modifiedDate;
     }
 
-    /**
-     * @param vote - The vote to remove from the list of votes on this bill.
-     */
-    public void removeVote(Vote vote, String version)
-    {
-        if (hasAmendment(version)) {
-            getAmendment(version).getVotes().remove(vote);
-        } else {
-            throw new IllegalArgumentException(INVALID_AMENDMENT_VERSION+version);
-        }
-    }
-
     @Override
-    public boolean equals(Object obj)
-    {
+    public boolean equals(Object obj) {
         if (obj != null && obj instanceof Bill) {
             Bill other = (Bill)obj;
             return this.getBillId().equals(other.getBillId());
@@ -705,8 +339,7 @@ public boolean equals(Object obj)
     }
 
     @Override
-    public int compareTo(Bill bill)
-    {
+    public int compareTo(Bill bill) {
         return this.getBillId().compareTo(bill.getBillId());
     }
 
@@ -716,8 +349,7 @@ public int compareTo(Bill bill)
      * @param term the term being searched
      * @return formatted bill number or original term
      */
-    public static String formatBillNo(String term)
-    {
+    public static String formatBillNo(String term) {
         Pattern p = Pattern.compile("^((?i)[sajr]\\W?0*\\d+[a-zA-Z]?)(?:\\-)?(\\d{4})?$");
         Matcher m = p.matcher(term);
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBill.java
f34337b79df7ecb4599710bffbf4c3c945dff8cc
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBillAction.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBillAction.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
import org.joda.time.LocalDate;
 * Represents a single action on a single bill. E.g. REFERRED TO RULES.*
    /** Print number of the base bill. */
    protected String baseBillPrintNo = "";
    /** The bill amendment version the action was taken on. */
    private String amendmentVersion;

    /** The date this action was performed. Has no time component. */
    /** The text of this action. */
    /** --- Constructors --- */
    public BillAction() {
        this.setBaseBillPrintNo(bill.getPrintNo());
        this.setAmendmentVersion(billAmendment);
    /** --- Functional Getters/Setters --- */
    public int getYear() {
        return new LocalDate(date).getYear();
    /** --- Overrides --- */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BillAction that = (BillAction) o;
        if (baseBillPrintNo != null ? !baseBillPrintNo.equals(that.baseBillPrintNo) : that.baseBillPrintNo != null)
            return false;
        if (amendmentVersion != null ? !amendmentVersion.equals(that.amendmentVersion) : that.amendmentVersion != null) return false;
        if (date != null ? !date.equals(that.date) : that.date != null) return false;
        if (text != null ? !text.equals(that.text) : that.text != null) return false;
        return true;
    @Override
    public int hashCode() {
        int result = baseBillPrintNo != null ? baseBillPrintNo.hashCode() : 0;
        result = 31 * result + (amendmentVersion != null ? amendmentVersion.hashCode() : 0);
        result = 31 * result + (date != null ? date.hashCode() : 0);
        result = 31 * result + (text != null ? text.hashCode() : 0);
        return result;
    public String toString() {
        return date.toString() + " " + text;
    /** --- Helper classes --- */
    /** --- Basic Getters/Setters --- */

    public String getBaseBillPrintNo() {
        return baseBillPrintNo;
    }

    public void setBaseBillPrintNo(String baseBillPrintNo) {
        this.baseBillPrintNo = baseBillPrintNo;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    public String getAmendmentVersion() {
        return amendmentVersion;
    public void setAmendmentVersion(String amendmentVersion) {
        this.amendmentVersion = amendmentVersion;
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
this.setBaseBillPrintNo(bill.getPrintNo());
        this.setAmendmentVersion(billAmendment);
    /** --- Functional Getters/Setters --- */
    public int getYear() {
        return new LocalDate(date).getYear();
    /** --- Overrides --- */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BillAction that = (BillAction) o;
        if (baseBillPrintNo != null ? !baseBillPrintNo.equals(that.baseBillPrintNo) : that.baseBillPrintNo != null)
            return false;
        if (amendmentVersion != null ? !amendmentVersion.equals(that.amendmentVersion) : that.amendmentVersion != null) return false;
        if (date != null ? !date.equals(that.date) : that.date != null) return false;
        if (text != null ? !text.equals(that.text) : that.text != null) return false;
        return true;
    @Override
    public int hashCode() {
        int result = baseBillPrintNo != null ? baseBillPrintNo.hashCode() : 0;
        result = 31 * result + (amendmentVersion != null ? amendmentVersion.hashCode() : 0);
        result = 31 * result + (date != null ? date.hashCode() : 0);
        result = 31 * result + (text != null ? text.hashCode() : 0);
        return result;
    public String toString() {
        return date.toString() + " " + text;
    /** --- Helper classes --- */
    /** --- Basic Getters/Setters --- */

    public String getBaseBillPrintNo() {
        return baseBillPrintNo;
public BillAction()
String baseBillPrintNo
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
this.baseBillPrintNo = baseBillPrintNo;
void setBaseBillPrintNo(String baseBillPrintNo)
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
return date;
Date getDate()
Date date
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
this.date = date;
void setDate(Date date)
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
return text;
String getText()
 * Represents a single action on a single bill. E.g. REFERRED TO RULES
 *
 *
 * @author GraylinKim
    /**
     * This object's unique object id. Bill+Date.getTime()+Text.
     */
    private String oid = "";
    /**
     * The date this action was performed. Has no time component.
     */
    /**
     * The text of this action.
     */
    /**
     * The bill this action was taken on.
     */
    private Bill bill = null;


    private String billVersion;

    /**
     * JavaBean constructor.
     */
    public BillAction()
    {
        this.bill = bill;
        this.setBillAmendment(billAmendment);
        this.oid = this.bill.getBillId() + "-" + this.date.getTime() + "-" + this.text;
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.setTime(date);
        this.setYear(calendar.get(java.util.Calendar.YEAR));
    /**
     * @return - The object's otype
     */
    public String getOtype()
    {
        return "action";
    }
    /**
     * @return - This object's unique object id.
     */
    public String getOid()
    {
        return this.oid;
    }

    /**
     * @param oid - The object's new oid.
     */
    public void setOid(String oid) {
        this.oid = oid;
    /**
     * @return - The date of the action.
     */
    public Date getDate() {
        return date;
    }
    /**
     * @param date - The new action date
     */
    public void setDate(Date date) {
        this.date = date;
    }
    /**
     * @return - The text of the action.
     */
    public String getText() {
        return text;
    }
    /**
     * @param text - The text of the action.
     */
    public void setText(String text) {
        this.text = text;
    }
    /**
     * @return - The bill the action was performed on.
     */
    public Bill getBill() {
        return bill;
    /**
     * @param bill - The new bill to this action targets.
     */
    public void setBill(Bill bill) {
        this.bill = bill;
    public boolean equals(Object obj) {
        if (obj != null && obj instanceof BillAction) {
            BillAction other = (BillAction)obj;
            return this.getOid().equals(other.getOid());
        }
        else {
            return false;
        }
    public int getYear() {
        java.util.Calendar cal = java.util.Calendar.getInstance();
        cal.setTime(date);
        return cal.get(java.util.Calendar.YEAR);
    }
    @Override
    public String toString() {
        return date.toString()+" "+text;
    public String getBillAmendment()
    {
        return billVersion;
    public void setBillAmendment(String billVersion)
    {
        this.billVersion = billVersion;
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
this.bill = bill;
        this.setBillAmendment(billAmendment);
        this.oid = this.bill.getBillId() + "-" + this.date.getTime() + "-" + this.text;
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.setTime(date);
        this.setYear(calendar.get(java.util.Calendar.YEAR));
    /**
     * @return - The object's otype
     */
    public String getOtype()
    {
        return "action";
public BillAction()
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
return this.oid;
String getOid()
String oid
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
this.oid = oid;
    /**
     * @return - The date of the action.
     */
    public Date getDate() {
        return date;
void setOid(String oid)
Date date
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
this.date = date;
void setDate(Date date)
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
return text;
String getText()
String text
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
this.text = text;
void setText(String text)
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
return bill;
    /**
     * @param bill - The new bill to this action targets.
     */
    public void setBill(Bill bill) {
        this.bill = bill;
    public boolean equals(Object obj) {
        if (obj != null && obj instanceof BillAction) {
            BillAction other = (BillAction)obj;
            return this.getOid().equals(other.getOid());
Bill getBill()
src/main/java/gov/nysenate/openleg/model/bill/BillAction.java
java.util.Calendar cal = java.util.Calendar.getInstance();
        cal.setTime(date);
        return cal.get(java.util.Calendar.YEAR);
int getYear()
@@ -1,48 +1,32 @@
 package gov.nysenate.openleg.model.bill;
 
 import gov.nysenate.openleg.model.BaseObject;
+import org.joda.time.LocalDate;
 
 import java.util.Comparator;
 import java.util.Date;
 
 /**
- * Represents a single action on a single bill. E.g. REFERRED TO RULES
- *
+ * Represents a single action on a single bill. E.g. REFERRED TO RULES.*
  * Uniquely identified by Bill+Date.getTime()+Text.
- *
- * @author GraylinKim
  */
 public class BillAction extends BaseObject
 {
-    /**
-     * This object's unique object id. Bill+Date.getTime()+Text.
-     */
-    private String oid = "";
+    /** Print number of the base bill. */
+    protected String baseBillPrintNo = "";
 
-    /**
-     * The date this action was performed. Has no time component.
-     */
+    /** The bill amendment version the action was taken on. */
+    private String amendmentVersion;
+
+    /** The date this action was performed. Has no time component. */
     private Date date = null;
 
-    /**
-     * The text of this action.
-     */
+    /** The text of this action. */
     private String text = "";
 
-    /**
-     * The bill this action was taken on.
-     */
-    private Bill bill = null;
-
+    /** --- Constructors --- */
 
-
-    private String billVersion;
-
-    /**
-     * JavaBean constructor.
-     */
-    public BillAction()
-    {
+    public BillAction() {
         super();
     }
 
@@ -55,100 +39,54 @@ public BillAction()
      */
     public BillAction(Date date, String text, Bill bill, String billAmendment) {
         super();
-        this.bill = bill;
-        this.setBillAmendment(billAmendment);
         this.date = date;
         this.text = text;
-        this.oid = this.bill.getBillId() + "-" + this.date.getTime() + "-" + this.text;
+        this.setBaseBillPrintNo(bill.getPrintNo());
+        this.setAmendmentVersion(billAmendment);
         this.setPublishDate(this.date);
         this.setModifiedDate(this.date);
         this.setSession(bill.getSession());
-        java.util.Calendar calendar = java.util.Calendar.getInstance();
-        calendar.setTime(date);
-        this.setYear(calendar.get(java.util.Calendar.YEAR));
     }
 
-    /**
-     * @return - The object's otype
-     */
-    public String getOtype()
-    {
-        return "action";
-    }
+    /** --- Functional Getters/Setters --- */
 
-    /**
-     * @return - This object's unique object id.
-     */
-    public String getOid()
-    {
-        return this.oid;
-    }
-
-    /**
-     * @param oid - The object's new oid.
-     */
-    public void setOid(String oid) {
-        this.oid = oid;
+    public int getYear() {
+        return new LocalDate(date).getYear();
     }
 
-    /**
-     * @return - The date of the action.
-     */
-    public Date getDate() {
-        return date;
-    }
+    /** --- Overrides --- */
 
-    /**
-     * @param date - The new action date
-     */
-    public void setDate(Date date) {
-        this.date = date;
-    }
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
 
-    /**
-     * @return - The text of the action.
-     */
-    public String getText() {
-        return text;
-    }
+        BillAction that = (BillAction) o;
 
-    /**
-     * @param text - The text of the action.
-     */
-    public void setText(String text) {
-        this.text = text;
-    }
+        if (baseBillPrintNo != null ? !baseBillPrintNo.equals(that.baseBillPrintNo) : that.baseBillPrintNo != null)
+            return false;
+        if (amendmentVersion != null ? !amendmentVersion.equals(that.amendmentVersion) : that.amendmentVersion != null) return false;
+        if (date != null ? !date.equals(that.date) : that.date != null) return false;
+        if (text != null ? !text.equals(that.text) : that.text != null) return false;
 
-    /**
-     * @return - The bill the action was performed on.
-     */
-    public Bill getBill() {
-        return bill;
+        return true;
     }
 
-    /**
-     * @param bill - The new bill to this action targets.
-     */
-    public void setBill(Bill bill) {
-        this.bill = bill;
+    @Override
+    public int hashCode() {
+        int result = baseBillPrintNo != null ? baseBillPrintNo.hashCode() : 0;
+        result = 31 * result + (amendmentVersion != null ? amendmentVersion.hashCode() : 0);
+        result = 31 * result + (date != null ? date.hashCode() : 0);
+        result = 31 * result + (text != null ? text.hashCode() : 0);
+        return result;
     }
 
     @Override
-    public boolean equals(Object obj) {
-        if (obj != null && obj instanceof BillAction) {
-            BillAction other = (BillAction)obj;
-            return this.getOid().equals(other.getOid());
-        }
-        else {
-            return false;
-        }
+    public String toString() {
+        return date.toString() + " " + text;
     }
 
-    public int getYear() {
-        java.util.Calendar cal = java.util.Calendar.getInstance();
-        cal.setTime(date);
-        return cal.get(java.util.Calendar.YEAR);
-    }
+    /** --- Helper classes --- */
 
     public static class ByEventDate implements Comparator<BillAction> {
         @Override
@@ -161,18 +99,37 @@ public int compare(BillAction be1, BillAction be2) {
         }
     }
 
-    @Override
-    public String toString() {
-        return date.toString()+" "+text;
+    /** --- Basic Getters/Setters --- */
+
+    public String getBaseBillPrintNo() {
+        return baseBillPrintNo;
+    }
+
+    public void setBaseBillPrintNo(String baseBillPrintNo) {
+        this.baseBillPrintNo = baseBillPrintNo;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
     }
 
-    public String getBillAmendment()
-    {
-        return billVersion;
+    public String getAmendmentVersion() {
+        return amendmentVersion;
     }
 
-    public void setBillAmendment(String billVersion)
-    {
-        this.billVersion = billVersion;
+    public void setAmendmentVersion(String amendmentVersion) {
+        this.amendmentVersion = amendmentVersion;
     }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBillAction.java
cc0de4e5269601390d7e784c9c1991c88c4881bd
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBillAmendment.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBillAmendment.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
import gov.nysenate.openleg.model.entity.Person;
import java.util.HashSet;
import java.util.Set;
    /** The "sameAs" bill in the other chamber that matches this version.
     *  There can be multiple same as bills in some cases, typically just 0 or 1 though. */
    protected Set<BillId> sameAs = new HashSet<>();
    public BillAmendment(Bill baseBill, String version) {
        this.baseBillPrintNo = baseBill.getPrintNo();
    /** --- Functional Getters/Setters --- */

    public String getBillId() {
        return this.baseBillPrintNo + this.version + "-" + this.session;
    public void setVersion(String version) {
        this.version = version.trim().toUpperCase();
    public void updateVote(Vote vote) {
        this.getVotes().remove(vote);
        this.getVotes().add(vote);
    }

    /** --- Basic Getters/Setters --- */

    public String getVersion() {
    public Set<BillId> getSameAs() {
    public void setSameAs(Set<BillId> sameAs) {
    public String getMemo() {
    public void setMemo(String memo) {
    public String getActClause() {
    public void setActClause(String actClause) {
    public String getFulltext() {
    public void setFulltext(String fulltext) {
    public String getCurrentCommittee() {
    public void setCurrentCommittee(String currentCommittee) {
    public List<Person> getCoSponsors() {
    public void setCoSponsors(List<Person> coSponsors) {
    public List<Person> getMultiSponsors() {
    public void setMultiSponsors(List<Person> multiSponsors) {
    public Boolean isStricken() {
    public void setStricken(Boolean stricken) {
    public Boolean isUniBill() {
    public void setUniBill(Boolean uniBill) {
}
Bill baseBill  String version
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
this.baseBillPrintNo = baseBill.getPrintNo();
    /** --- Functional Getters/Setters --- */

    public String getBillId() {
        return this.baseBillPrintNo + this.version + "-" + this.session;
    public void setVersion(String version) {
        this.version = version.trim().toUpperCase();
    public void updateVote(Vote vote) {
        this.getVotes().remove(vote);
        this.getVotes().add(vote);
public BillAmendment(Bill baseBill, String version)
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
public Set<BillId> getSameAs() {
    public void setSameAs(Set<BillId> sameAs) {
    public String getMemo() {
    public void setMemo(String memo) {
    public String getActClause() {
    public void setActClause(String actClause) {
    public String getFulltext() {
    public void setFulltext(String fulltext) {
    public String getCurrentCommittee() {
    public void setCurrentCommittee(String currentCommittee) {
    public List<Person> getCoSponsors() {
    public void setCoSponsors(List<Person> coSponsors) {
    public List<Person> getMultiSponsors() {
    public void setMultiSponsors(List<Person> multiSponsors) {
    public Boolean isStricken() {
    public void setStricken(Boolean stricken) {
    public Boolean isUniBill() {
    public void setUniBill(Boolean uniBill) {
String getVersion()
import gov.nysenate.openleg.model.Person;
import gov.nysenate.openleg.model.Vote;
    /** Id of the base bill. */
    protected String baseBillId = "";

    /** The "sameAs" bill in the other chamber that matches this version. */
    protected String sameAs = "";
    /** The law code of the amendment. */
    protected String law = "";

    public BillAmendment(Bill baseBill, String version)
    {
        this.baseBillPrintNo = baseBill.getPrintNumber();
        this.baseBillId = baseBill.getBillId();
    public String getBaseBillId()
    {
        return baseBillId;
    public void setBaseBillId(String baseBillId)
    {
        this.baseBillId = baseBillId;
    public String getBillId()
    {
        return this.baseBillPrintNo + this.version + "-" + this.session;
    }

    public String getVersion()
    {
    public void setVersion(String version)
    {
        this.version = version.trim().toUpperCase();
    }

    public String getSameAs()
    {
    public void setSameAs(String sameAs)
    {
    public String getMemo()
    {
    public void setMemo(String memo)
    {
    public String getLaw()
    {
        return law;
    }

    public void setLaw(String law)
    {
        this.law = law;
    }

    public String getActClause()
    {
    public void setActClause(String actClause)
    {
    public String getFulltext()
    {
    public void setFulltext(String fulltext)
    {
    public String getCurrentCommittee()
    {
    public void setCurrentCommittee(String currentCommittee)
    {
    public List<Person> getCoSponsors()
    {
    public void setCoSponsors(List<Person> coSponsors)
    {
    public List<Person> getMultiSponsors()
    {
    public void setMultiSponsors(List<Person> multiSponsors)
    {
    public Boolean isStricken()
    {
    public void setStricken(Boolean stricken)
    {
    @Override
    public String getOid()
    {
        return this.getBillId();
    }

    @Override
    public String getOtype()
    {
        return "billamendment";
    }

    public Boolean isUniBill()
    {
    public void setUniBill(Boolean uniBill)
    {
}
Bill baseBill  String version
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
this.baseBillPrintNo = baseBill.getPrintNumber();
        this.baseBillId = baseBill.getBillId();
    public String getBaseBillId()
    {
        return baseBillId;
    public void setBaseBillId(String baseBillId)
    {
        this.baseBillId = baseBillId;
    public String getBillId()
    {
        return this.baseBillPrintNo + this.version + "-" + this.session;
public BillAmendment(Bill baseBill, String version)
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
public void setVersion(String version)
    {
        this.version = version.trim().toUpperCase();
String getVersion()
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
public void setSameAs(String sameAs)
    {
    public String getMemo()
    {
    public void setMemo(String memo)
    {
    public String getLaw()
    {
        return law;
String getSameAs()
String law
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
this.law = law;
void setLaw(String law)
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
public void setActClause(String actClause)
    {
    public String getFulltext()
    {
    public void setFulltext(String fulltext)
    {
    public String getCurrentCommittee()
    {
    public void setCurrentCommittee(String currentCommittee)
    {
    public List<Person> getCoSponsors()
    {
    public void setCoSponsors(List<Person> coSponsors)
    {
    public List<Person> getMultiSponsors()
    {
    public void setMultiSponsors(List<Person> multiSponsors)
    {
    public Boolean isStricken()
    {
    public void setStricken(Boolean stricken)
    {
    @Override
    public String getOid()
    {
        return this.getBillId();
String getActClause()
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
return "billamendment";
String getOtype()
src/main/java/gov/nysenate/openleg/model/bill/BillAmendment.java
public void setUniBill(Boolean uniBill)
    {
Boolean isUniBill()
@@ -1,32 +1,28 @@
 package gov.nysenate.openleg.model.bill;
 
 import gov.nysenate.openleg.model.BaseObject;
-import gov.nysenate.openleg.model.Person;
-import gov.nysenate.openleg.model.Vote;
+import gov.nysenate.openleg.model.entity.Person;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 public class BillAmendment extends BaseObject
 {
-    /** Id of the base bill. */
-    protected String baseBillId = "";
-
     /** Print number of the base bill. */
     protected String baseBillPrintNo = "";
 
     /** Amendment version, (e.g "A"). */
     protected String version = "";
 
-    /** The "sameAs" bill in the other chamber that matches this version. */
-    protected String sameAs = "";
+    /** The "sameAs" bill in the other chamber that matches this version.
+     *  There can be multiple same as bills in some cases, typically just 0 or 1 though. */
+    protected Set<BillId> sameAs = new HashSet<>();
 
     /** The sponsor memo of the amendment. */
     protected String memo = "";
 
-    /** The law code of the amendment. */
-    protected String law = "";
-
     /** The AN ACT TO... clause for the bill version. */
     protected String actClause = "";
 
@@ -55,26 +51,31 @@ public BillAmendment() {
         super();
     }
 
-    public BillAmendment(Bill baseBill, String version)
-    {
+    public BillAmendment(Bill baseBill, String version) {
         this();
-        this.baseBillPrintNo = baseBill.getPrintNumber();
-        this.baseBillId = baseBill.getBillId();
+        this.baseBillPrintNo = baseBill.getPrintNo();
         this.session = baseBill.getSession();
         this.year = baseBill.getYear();
         this.version = version;
     }
 
-    public String getBaseBillId()
-    {
-        return baseBillId;
+    /** --- Functional Getters/Setters --- */
+
+    public String getBillId() {
+        return this.baseBillPrintNo + this.version + "-" + this.session;
     }
 
-    public void setBaseBillId(String baseBillId)
-    {
-        this.baseBillId = baseBillId;
+    public void setVersion(String version) {
+        this.version = version.trim().toUpperCase();
     }
 
+    public void updateVote(Vote vote) {
+        this.getVotes().remove(vote);
+        this.getVotes().add(vote);
+    }
+
+    /** --- Basic Getters/Setters --- */
+
     public String getBaseBillPrintNo() {
         return baseBillPrintNo;
     }
@@ -83,108 +84,71 @@ public void setBaseBillPrintNo(String baseBillPrintNo) {
         this.baseBillPrintNo = baseBillPrintNo;
     }
 
-    public String getBillId()
-    {
-        return this.baseBillPrintNo + this.version + "-" + this.session;
-    }
-
-    public String getVersion()
-    {
+    public String getVersion() {
         return version;
     }
 
-    public void setVersion(String version)
-    {
-        this.version = version.trim().toUpperCase();
-    }
-
-    public String getSameAs()
-    {
+    public Set<BillId> getSameAs() {
         return sameAs;
     }
 
-    public void setSameAs(String sameAs)
-    {
+    public void setSameAs(Set<BillId> sameAs) {
         this.sameAs = sameAs;
     }
 
-    public String getMemo()
-    {
+    public String getMemo() {
         return memo;
     }
 
-    public void setMemo(String memo)
-    {
+    public void setMemo(String memo) {
         this.memo = memo;
     }
 
-    public String getLaw()
-    {
-        return law;
-    }
-
-    public void setLaw(String law)
-    {
-        this.law = law;
-    }
-
-    public String getActClause()
-    {
+    public String getActClause() {
         return actClause;
     }
 
-    public void setActClause(String actClause)
-    {
+    public void setActClause(String actClause) {
         this.actClause = actClause;
     }
 
-    public String getFulltext()
-    {
+    public String getFulltext() {
         return fulltext;
     }
 
-    public void setFulltext(String fulltext)
-    {
+    public void setFulltext(String fulltext) {
         this.fulltext = fulltext;
     }
 
-    public String getCurrentCommittee()
-    {
+    public String getCurrentCommittee() {
         return currentCommittee;
     }
 
-    public void setCurrentCommittee(String currentCommittee)
-    {
+    public void setCurrentCommittee(String currentCommittee) {
         this.currentCommittee = currentCommittee;
     }
 
-    public List<Person> getCoSponsors()
-    {
+    public List<Person> getCoSponsors() {
         return coSponsors;
     }
 
-    public void setCoSponsors(List<Person> coSponsors)
-    {
+    public void setCoSponsors(List<Person> coSponsors) {
         this.coSponsors = coSponsors;
     }
 
-    public List<Person> getMultiSponsors()
-    {
+    public List<Person> getMultiSponsors() {
         return multiSponsors;
     }
 
-    public void setMultiSponsors(List<Person> multiSponsors)
-    {
+    public void setMultiSponsors(List<Person> multiSponsors) {
         this.multiSponsors = multiSponsors;
     }
 
-    public Boolean isStricken()
-    {
+    public Boolean isStricken() {
         return stricken;
     }
 
-    public void setStricken(Boolean stricken)
-    {
+    public void setStricken(Boolean stricken) {
         this.stricken = stricken;
     }
 
@@ -196,25 +160,11 @@ public void setVotes(List<Vote> votes) {
         this.votes = votes;
     }
 
-    @Override
-    public String getOid()
-    {
-        return this.getBillId();
-    }
-
-    @Override
-    public String getOtype()
-    {
-        return "billamendment";
-    }
-
-    public Boolean isUniBill()
-    {
+    public Boolean isUniBill() {
         return uniBill;
     }
 
-    public void setUniBill(Boolean uniBill)
-    {
+    public void setUniBill(Boolean uniBill) {
         this.uniBill = uniBill;
     }
-}
+}
\ No newline at end of file
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FBillAmendment.java
969c931ef28ac0b3bdd991fe2c59028efe406287
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FVote.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FVote.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/bill/Vote.java
import gov.nysenate.openleg.model.entity.Person;
import org.joda.time.LocalDate;
public class Vote extends BaseObject
{
    public int count() {
        return ayes.size() + nays.size() + abstains.size() + excused.size();
    public Vote() {
        ayes = new ArrayList<>();
        ayeswr = new ArrayList<>();
        nays = new ArrayList<>();
        abstains = new ArrayList<>();
        excused = new ArrayList<>();
        absent = new ArrayList<>();
    public Vote(String billId, String billAmendment, Date date, int type, String sequenceNumber) {
    public Vote(Bill bill, String billAmendment, Date date, int type, String sequenceNumber) {
    public Vote(BillAmendment amendment, Date date, int type, String sequenceNumber) {
        this();
        this.voteDate = date;
        this.setYear(new LocalDate(voteDate).getYear());
        this.setSession(this.getYear() % 2 == 0 ? this.getYear() - 1 : this.getYear());
        this.voteType = type;
        this.setSequenceNumber(sequenceNumber);
    /** --- Basic Getters/Setters --- */

    public int getVoteType()
        return voteType;
import gov.nysenate.openleg.model.person.Person;
public class Vote extends BaseObject {

    public int count()
    {
        return ayes.size()+nays.size()+abstains.size()+excused.size();
    public Vote()
    {
        ayes = new ArrayList<String>();
        ayeswr = new ArrayList<String>();
        nays = new ArrayList<String>();
        abstains = new ArrayList<String>();
        excused = new ArrayList<String>();
        absent = new ArrayList<String>();
    public Vote(String billId, String billAmendment, Date date, int type, String sequenceNumber)
    {
    public Vote(Bill bill, String billAmendment, Date date, int type, String sequenceNumber)
    {
    public int getVoteType()
    {
        return voteType;
    /**
     * The object type of the bill.
     */
    public String getOtype()
        return "vote";
@@ -1,15 +1,16 @@
 package gov.nysenate.openleg.model.bill;
 
 import gov.nysenate.openleg.model.BaseObject;
-import gov.nysenate.openleg.model.person.Person;
+import gov.nysenate.openleg.model.entity.Person;
+import org.joda.time.LocalDate;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
-public class Vote extends BaseObject {
-
+public class Vote extends BaseObject
+{
     public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
 
     private int voteType;
@@ -42,24 +43,21 @@ public class Vote extends BaseObject {
 
     public final static int VOTE_TYPE_COMMITTEE = 2;
 
-    public int count()
-    {
-        return ayes.size()+nays.size()+abstains.size()+excused.size();
+    public int count() {
+        return ayes.size() + nays.size() + abstains.size() + excused.size();
     }
 
-    public Vote()
-    {
+    public Vote() {
         super();
-        ayes = new ArrayList<String>();
-        ayeswr = new ArrayList<String>();
-        nays = new ArrayList<String>();
-        abstains = new ArrayList<String>();
-        excused = new ArrayList<String>();
-        absent = new ArrayList<String>();
+        ayes = new ArrayList<>();
+        ayeswr = new ArrayList<>();
+        nays = new ArrayList<>();
+        abstains = new ArrayList<>();
+        excused = new ArrayList<>();
+        absent = new ArrayList<>();
     }
 
-    public Vote(String billId, String billAmendment, Date date, int type, String sequenceNumber)
-    {
+    public Vote(String billId, String billAmendment, Date date, int type, String sequenceNumber) {
         this();
         this.voteDate = date;
         java.util.Calendar cal = java.util.Calendar.getInstance();
@@ -71,23 +69,25 @@ public Vote(String billId, String billAmendment, Date date, int type, String seq
         this.oid = billId+'-'+dateFormat.format(voteDate)+'-'+String.valueOf(voteType)+'-'+sequenceNumber;
     }
 
-    public Vote(Bill bill, String billAmendment, Date date, int type, String sequenceNumber)
-    {
+    public Vote(Bill bill, String billAmendment, Date date, int type, String sequenceNumber) {
         this(bill.getBillId(), billAmendment, date, type, sequenceNumber);
         this.bill = bill;
     }
 
-    public int getVoteType()
-    {
-        return voteType;
+    public Vote(BillAmendment amendment, Date date, int type, String sequenceNumber) {
+        this();
+        this.voteDate = date;
+        this.setYear(new LocalDate(voteDate).getYear());
+        this.setSession(this.getYear() % 2 == 0 ? this.getYear() - 1 : this.getYear());
+        this.voteType = type;
+        this.setSequenceNumber(sequenceNumber);
     }
 
-    /**
-     * The object type of the bill.
-     */
-    public String getOtype()
+    /** --- Basic Getters/Setters --- */
+
+    public int getVoteType()
     {
-        return "vote";
+        return voteType;
     }
 
     public String getOid()
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fbill%2FVote.java
58a200055f793c788ac2a313828b11041ee7014f
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendar.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendar.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/calendar/Calendar.java
    @Override
    public String getOid()
    {
        return "calendar-"+this.getYear()+"-"+this.getNumber();
    }

    @Override
    public String getOtype()
    {
        return "calendar";
    }

src/main/java/gov/nysenate/openleg/model/calendar/Calendar.java
return "calendar-"+this.getYear()+"-"+this.getNumber();
String getOid()
src/main/java/gov/nysenate/openleg/model/calendar/Calendar.java
return "calendar";
String getOtype()
@@ -27,18 +27,6 @@ public Calendar(Integer number, Integer session, Integer year)
         this.setYear(year);
     }
 
-    @Override
-    public String getOid()
-    {
-        return "calendar-"+this.getYear()+"-"+this.getNumber();
-    }
-
-    @Override
-    public String getOtype()
-    {
-        return "calendar";
-    }
-
     public Integer getNumber()
     {
         return number;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendar.java
ece6ad3c563cab525a3b9f19a66879246742e16a
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendarActiveList.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendarActiveList.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/calendar/CalendarActiveList.java
import gov.nysenate.openleg.util.DateHelper;

    @Override
    public String getOid()
    {
        return this.getOtype()+"-"+DateHelper.LRS_DATE_ONLY_FORMAT.format(this.getCalDate())+"-"+this.getId();
    }

    @Override
    public String getOtype()
    {
        return "calendar-active";
    }
src/main/java/gov/nysenate/openleg/model/calendar/CalendarActiveList.java
return this.getOtype()+"-"+DateHelper.LRS_DATE_ONLY_FORMAT.format(this.getCalDate())+"-"+this.getId();
String getOid()
src/main/java/gov/nysenate/openleg/model/calendar/CalendarActiveList.java
return "calendar-active";
String getOtype()
@@ -1,7 +1,6 @@
 package gov.nysenate.openleg.model.calendar;
 
 import gov.nysenate.openleg.model.BaseObject;
-import gov.nysenate.openleg.util.DateHelper;
 
 import java.util.ArrayList;
 import java.util.Date;
@@ -83,16 +82,4 @@ public void setReleaseDateTime(Date releaseDateTime)
     {
         this.releaseDateTime = releaseDateTime;
     }
-
-    @Override
-    public String getOid()
-    {
-        return this.getOtype()+"-"+DateHelper.LRS_DATE_ONLY_FORMAT.format(this.getCalDate())+"-"+this.getId();
-    }
-
-    @Override
-    public String getOtype()
-    {
-        return "calendar-active";
-    }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendarActiveList.java
e8bdbb2cda0ff957c4c276c6d7d14997d231b99f
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendarSupplemental.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendarSupplemental.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/calendar/CalendarSupplemental.java
import gov.nysenate.openleg.util.DateHelper;
    @Override
    public String getOid()
    {
        return this.getOtype()+"-"+DateHelper.LRS_DATE_ONLY_FORMAT.format(this.getCalDate())+"-"+this.getId();
    }

    @Override
    public String getOtype()
    {
        return "calendar-supplemental";
    }

src/main/java/gov/nysenate/openleg/model/calendar/CalendarSupplemental.java
return this.getOtype()+"-"+DateHelper.LRS_DATE_ONLY_FORMAT.format(this.getCalDate())+"-"+this.getId();
String getOid()
src/main/java/gov/nysenate/openleg/model/calendar/CalendarSupplemental.java
return "calendar-supplemental";
String getOtype()
@@ -1,7 +1,6 @@
 package gov.nysenate.openleg.model.calendar;
 
 import gov.nysenate.openleg.model.BaseObject;
-import gov.nysenate.openleg.util.DateHelper;
 
 import java.util.Date;
 import java.util.LinkedHashMap;
@@ -27,18 +26,6 @@ public CalendarSupplemental(String id, Date calDate, Date releaseDateTime)
         this.setReleaseDateTime(releaseDateTime);
     }
 
-    @Override
-    public String getOid()
-    {
-        return this.getOtype()+"-"+DateHelper.LRS_DATE_ONLY_FORMAT.format(this.getCalDate())+"-"+this.getId();
-    }
-
-    @Override
-    public String getOtype()
-    {
-        return "calendar-supplemental";
-    }
-
     public String getId()
     {
         return id;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fcalendar%2FCalendarSupplemental.java
795ee8e03026a3c6e44203c10798bc01fd9d0199
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIBlock.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIBlock.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
import gov.nysenate.openleg.model.bill.BillId;

    /** The file name of the fragment that generated this block. */
    private String fragmentFileName;

    /** The type of SOBIFragment that generated this block. */
    private SOBIFragmentType fragmentType;

    /** The bill identifier. */
    private BillId billId;
     * assumed to be valid sobi file and is NOT checked for performance reasons.
        this.setBillId(line.substring(4,10), line.substring(10,11), Integer.parseInt(line.substring(0,4)));
    public SOBIBlock(String fragmentFileName, SOBIFragmentType type, int startLineNo, String line) {
        this.fragmentFileName = fragmentFileName;
        this.fragmentType = type;
        return (this.fragmentFileName + ":" + this.getStartLineNo() + "-" + this.getEndLineNo());
     * Returns the session year of the bill.
     */
    public int getYear() {
        return (this.billId != null) ? this.billId.getSession() : -1;
    }

    /**
     * Returns the print no of the base bill.
     */
    public String getBasePrintNo() {
        return (this.billId != null) ? this.billId.getBasePrintNo() : null;
    }

    /**
     * Returns the amendment version.
     */
    public String getAmendment() {
        return (this.billId != null) ? this.billId.getVersion() : null;
    }

    /**
     * Sets the BillId for this block.
     * @param printNo String
     * @param version String
     * @param session int
    public void setBillId(String printNo, String version, int session) {
        this.billId = new BillId(printNo.substring(0,1) + Integer.parseInt(printNo.substring(1)),
                                 session, version.trim());
    public String getFragmentFileName() {
        return fragmentFileName;
    }

    public void setFragmentFileName(String fragmentFileName) {
        this.fragmentFileName = fragmentFileName;
    }

    public SOBIFragmentType getFragmentType() {
        return fragmentType;
    }

    public void setFragmentType(SOBIFragmentType fragmentType) {
        this.fragmentType = fragmentType;
    }

String fragmentFileName  SOBIFragmentType type  int startLineNo  String line
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
this.fragmentFileName = fragmentFileName;
        this.fragmentType = type;
        return (this.fragmentFileName + ":" + this.getStartLineNo() + "-" + this.getEndLineNo());
     * Returns the session year of the bill.
     */
    public int getYear() {
        return (this.billId != null) ? this.billId.getSession() : -1;
public SOBIBlock(String fragmentFileName, SOBIFragmentType type, int startLineNo, String line)
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
return (this.billId != null) ? this.billId.getBasePrintNo() : null;
String getBasePrintNo()
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
return (this.billId != null) ? this.billId.getVersion() : null;
String getAmendment()
String printNo  String version  int session
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
this.billId = new BillId(printNo.substring(0,1) + Integer.parseInt(printNo.substring(1)),
                                 session, version.trim());
    public String getFragmentFileName() {
        return fragmentFileName;
void setBillId(String printNo, String version, int session)
String fragmentFileName
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
this.fragmentFileName = fragmentFileName;
void setFragmentFileName(String fragmentFileName)
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
return fragmentType;
SOBIFragmentType getFragmentType()
SOBIFragmentType fragmentType
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
this.fragmentType = fragmentType;
void setFragmentType(SOBIFragmentType fragmentType)
import java.io.File;
    /** The file from which the block was found. Defaults to null when using the basic constructor. */
    private File file = null;

    /** The year indicated in the block header. */
    private Integer year = 0;

    /** The printNo of the base bill. The amendment character is NOT included. */
    private String printNo = "";

    /** The amendment character for the base bill. Can be empty or single letter string from "A-Z" */
    private String amendment = "";
     * assumed to be valid SOBIFile file and is NOT checked for performance reasons.
        this.setYear(Integer.parseInt(line.substring(0,4)));
        this.setPrintNo(line.substring(4,10));
        this.setAmendment(line.substring(10,11).trim());
    public SOBIBlock(File file, int startLineNo, String line) {
        this.setFile(file);
        return (this.getFile() == null ? "null" : this.getFile().getName())+":"+this.getStartLineNo();
     * Strips all leading zeros from the numerical part of the print number:
     * <p>
     * A00370 -> A370
     *
     * @param printNo
    public void setPrintNo(String printNo) {
        this.printNo = printNo.substring(0,1)+Integer.parseInt(printNo.substring(1));
    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public String getPrintNo() {
        return printNo;
    }

    public String getAmendment() {
        return amendment;
    }

    public void setAmendment(String amendment) {
        this.amendment = amendment;
    }

File file  int startLineNo  String line
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
this.setFile(file);
        return (this.getFile() == null ? "null" : this.getFile().getName())+":"+this.getStartLineNo();
     * Strips all leading zeros from the numerical part of the print number:
     * <p>
     * A00370 -> A370
     *
     * @param printNo
    public void setPrintNo(String printNo) {
        this.printNo = printNo.substring(0,1)+Integer.parseInt(printNo.substring(1));
    public File getFile() {
        return file;
public SOBIBlock(File file, int startLineNo, String line)
File file
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
this.file = file;
void setFile(File file)
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
return year;
int getYear()
int year
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
this.year = year;
void setYear(int year)
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
return printNo;
String getPrintNo()
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
return amendment;
String getAmendment()
String amendment
src/main/java/gov/nysenate/openleg/model/sobi/SOBIBlock.java
this.amendment = amendment;
void setAmendment(String amendment)
@@ -1,6 +1,7 @@
 package gov.nysenate.openleg.model.sobi;
 
-import java.io.File;
+import gov.nysenate.openleg.model.bill.BillId;
+
 import java.util.Arrays;
 import java.util.List;
 import java.util.regex.Pattern;
@@ -38,29 +39,26 @@ public class SOBIBlock
     /** A pattern to verify that a string is in the SOBI line format. */
     public static final Pattern blockPattern = Pattern.compile("^[0-9]{4}[A-Z][0-9]{5}[ A-Z][1-9ABCMRTV]");
 
+    /** The file name of the fragment that generated this block. */
+    private String fragmentFileName;
+
+    /** The type of SOBIFragment that generated this block. */
+    private SOBIFragmentType fragmentType;
+
     /** The line number that the block starts at. Defaults to zero when using the basic constructor. */
     private Integer startLineNo = 0;
 
     /** The line number that the block ends at. Defaults to zero when using the basic constructor. */
     private Integer endLineNo = 0;
 
-    /** The file from which the block was found. Defaults to null when using the basic constructor. */
-    private File file = null;
-
     /** The full line header from the line used to construct the Block. */
     private String header = "";
 
     /** The portion of the header containing just the bill designator, i.e not including line type. */
     private String billHeader = "";
 
-    /** The year indicated in the block header. */
-    private Integer year = 0;
-
-    /** The printNo of the base bill. The amendment character is NOT included. */
-    private String printNo = "";
-
-    /** The amendment character for the base bill. Can be empty or single letter string from "A-Z" */
-    private String amendment = "";
+    /** The bill identifier. */
+    private BillId billId;
 
     /** The sobi line type of the block. Determines how the data is interpreted. */
     private SOBILineType type;
@@ -76,13 +74,11 @@ public class SOBIBlock
 
     /**
      * Construct a new block with without location information from a valid SOBIFile line. The line is
-     * assumed to be valid SOBIFile file and is NOT checked for performance reasons.
+     * assumed to be valid sobi file and is NOT checked for performance reasons.
      */
     public SOBIBlock(String line) {
-        this.setYear(Integer.parseInt(line.substring(0,4)));
-        this.setPrintNo(line.substring(4,10));
-        this.setAmendment(line.substring(10,11).trim());
         this.setBillHeader(line.substring(0, 11));
+        this.setBillId(line.substring(4,10), line.substring(10,11), Integer.parseInt(line.substring(0,4)));
         this.setType(SOBILineType.valueOfCode(line.charAt(11)));
         this.setHeader(line.substring(0,12));
         this.setData(line.substring(12));
@@ -94,9 +90,10 @@ public SOBIBlock(String line) {
      * the source file and line number the block was initialized from. The line is assumed to be
      * valid SOBI file and is NOT checked for performance reasons.
      */
-    public SOBIBlock(File file, int startLineNo, String line) {
+    public SOBIBlock(String fragmentFileName, SOBIFragmentType type, int startLineNo, String line) {
         this(line);
-        this.setFile(file);
+        this.fragmentFileName = fragmentFileName;
+        this.fragmentType = type;
         this.setStartLineNo(startLineNo);
     }
 
@@ -135,7 +132,7 @@ public String toString() {
      * Returns a representation of the location of the block: fileName:lineNumber.
      */
     public String getLocation() {
-        return (this.getFile() == null ? "null" : this.getFile().getName())+":"+this.getStartLineNo();
+        return (this.fragmentFileName + ":" + this.getStartLineNo() + "-" + this.getEndLineNo());
     }
 
     /**
@@ -153,16 +150,37 @@ public void setData(String data) {
     }
 
     /**
-     * Strips all leading zeros from the numerical part of the print number:
-     * <p>
-     * A00370 -> A370
-     *
-     * @param printNo
+     * Returns the session year of the bill.
+     */
+    public int getYear() {
+        return (this.billId != null) ? this.billId.getSession() : -1;
+    }
+
+    /**
+     * Returns the print no of the base bill.
+     */
+    public String getBasePrintNo() {
+        return (this.billId != null) ? this.billId.getBasePrintNo() : null;
+    }
+
+    /**
+     * Returns the amendment version.
+     */
+    public String getAmendment() {
+        return (this.billId != null) ? this.billId.getVersion() : null;
+    }
+
+    /**
+     * Sets the BillId for this block.
+     * @param printNo String
+     * @param version String
+     * @param session int
      * @throws NumberFormatException on malformed print numbers
      */
-    public void setPrintNo(String printNo) {
+    public void setBillId(String printNo, String version, int session) {
         // Integer conversion removes leading zeros in the print number.
-        this.printNo = printNo.substring(0,1)+Integer.parseInt(printNo.substring(1));
+        this.billId = new BillId(printNo.substring(0,1) + Integer.parseInt(printNo.substring(1)),
+                                 session, version.trim());
     }
 
     /**
@@ -178,6 +196,22 @@ public void setEndLineNo(Integer endLineNo) {
 
     /** --- Basic Getters/Setters */
 
+    public String getFragmentFileName() {
+        return fragmentFileName;
+    }
+
+    public void setFragmentFileName(String fragmentFileName) {
+        this.fragmentFileName = fragmentFileName;
+    }
+
+    public SOBIFragmentType getFragmentType() {
+        return fragmentType;
+    }
+
+    public void setFragmentType(SOBIFragmentType fragmentType) {
+        this.fragmentType = fragmentType;
+    }
+
     public int getStartLineNo() {
         return startLineNo;
     }
@@ -190,14 +224,6 @@ public Integer getEndLineNo() {
         return endLineNo;
     }
 
-    public File getFile() {
-        return file;
-    }
-
-    public void setFile(File file) {
-        this.file = file;
-    }
-
     public String getHeader() {
         return header;
     }
@@ -206,26 +232,6 @@ public void setHeader(String header) {
         this.header = header;
     }
 
-    public int getYear() {
-        return year;
-    }
-
-    public void setYear(int year) {
-        this.year = year;
-    }
-
-    public String getPrintNo() {
-        return printNo;
-    }
-
-    public String getAmendment() {
-        return amendment;
-    }
-
-    public void setAmendment(String amendment) {
-        this.amendment = amendment;
-    }
-
     public SOBILineType getType() {
         return type;
     }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIBlock.java
c85249ab9b599869dd6d4fb7d5f3e98f43b3fe4f
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFile.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFile.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFile.java
import java.io.FileNotFoundException;
    /**
     * SOBI files are (mostly) in a CP850 or similar encoding. This was determined from the byte mapping of
     * paragraph/section characters to 244/245. This can't be 100% correct though because the degree symbol
     * must be 193 in the correct code set. See SOBI.D120612.T125850.TXT.
     */
    /** The datetime when the SOBI file was last staged for processing */
    private Date stagedDateTime;

    public SOBIFile(File file, String encoding) throws IOException {
        if (file.exists()) {
            this.fileName = file.getName();
            this.text = FileUtils.readFileToString(file, encoding);
                this.publishedDateTime = new Date(file.lastModified());
            throw new FileNotFoundException(file.getAbsolutePath());
        return "SOBIFile{" +
                "fileName='" + fileName + '\'' +
                ", processedCount=" + processedCount +
                ", processedDateTime=" + processedDateTime +
                ", publishedDateTime=" + publishedDateTime +
                ", pendingProcessing=" + pendingProcessing +
                '}';
    }

    /** --- Functional Getters/Setters --- */

    public void incrementProcessedCount() {
        this.processedCount++;
    public Date getStagedDateTime() {
        return stagedDateTime;
    }

    public void setStagedDateTime(Date stagedDateTime) {
        this.stagedDateTime = stagedDateTime;
    }

src/main/java/gov/nysenate/openleg/model/sobi/SOBIFile.java
this.processedCount++;
    public Date getStagedDateTime() {
        return stagedDateTime;
void incrementProcessedCount()
Date stagedDateTime
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFile.java
this.stagedDateTime = stagedDateTime;
void setStagedDateTime(Date stagedDateTime)
    public SOBIFile(File sobiFile, String encoding) throws IOException {
        if (sobiFile.exists()) {
            this.fileName = sobiFile.getName();
            this.text = FileUtils.readFileToString(sobiFile, encoding);
                this.publishedDateTime = new Date(sobiFile.lastModified());
            throw new IOException("Given sobiFile does not exist in the file system! (" + sobiFile.getAbsolutePath() + ")");
        return "SOBIFile {fileName='" + fileName + '\'' + ", processedCount=" + processedCount + '}';
@@ -3,6 +3,7 @@
 import org.apache.commons.io.FileUtils;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -17,6 +18,11 @@
  */
 public class SOBIFile
 {
+    /**
+     * SOBI files are (mostly) in a CP850 or similar encoding. This was determined from the byte mapping of
+     * paragraph/section characters to 244/245. This can't be 100% correct though because the degree symbol
+     * must be 193 in the correct code set. See SOBI.D120612.T125850.TXT.
+     */
     public static final String DEFAULT_ENCODING = "CP850";
 
     /** The format required for the SOBI file name. e.g. SOBI.D130323.T065432.TXT */
@@ -31,6 +37,9 @@ public class SOBIFile
     /** The datetime when the SOBI file was last processed */
     private Date processedDateTime;
 
+    /** The datetime when the SOBI file was last staged for processing */
+    private Date stagedDateTime;
+
     /** The actual text body of the file */
     private String text;
 
@@ -46,29 +55,41 @@ public SOBIFile(File sobiFile) throws IOException {
         this(sobiFile, DEFAULT_ENCODING);
     }
 
-    public SOBIFile(File sobiFile, String encoding) throws IOException {
-        if (sobiFile.exists()) {
-            this.fileName = sobiFile.getName();
-            this.text = FileUtils.readFileToString(sobiFile, encoding);
+    public SOBIFile(File file, String encoding) throws IOException {
+        if (file.exists()) {
+            this.fileName = file.getName();
+            this.text = FileUtils.readFileToString(file, encoding);
             this.processedCount = 0;
             this.pendingProcessing = false;
             try {
                 this.publishedDateTime = sobiDateFormat.parse(fileName);
             }
             catch (ParseException ex) {
-                this.publishedDateTime = new Date(sobiFile.lastModified());
+                this.publishedDateTime = new Date(file.lastModified());
                 ex.printStackTrace();
             }
         }
         else {
-            throw new IOException("Given sobiFile does not exist in the file system! (" + sobiFile.getAbsolutePath() + ")");
+            throw new FileNotFoundException(file.getAbsolutePath());
         }
     }
 
     /** --- Override Methods --- */
     @Override
     public String toString() {
-        return "SOBIFile {fileName='" + fileName + '\'' + ", processedCount=" + processedCount + '}';
+        return "SOBIFile{" +
+                "fileName='" + fileName + '\'' +
+                ", processedCount=" + processedCount +
+                ", processedDateTime=" + processedDateTime +
+                ", publishedDateTime=" + publishedDateTime +
+                ", pendingProcessing=" + pendingProcessing +
+                '}';
+    }
+
+    /** --- Functional Getters/Setters --- */
+
+    public void incrementProcessedCount() {
+        this.processedCount++;
     }
 
     /** --- Basic Getters/Setters --- */
@@ -97,6 +118,14 @@ public void setProcessedDateTime(Date processedDateTime) {
         this.processedDateTime = processedDateTime;
     }
 
+    public Date getStagedDateTime() {
+        return stagedDateTime;
+    }
+
+    public void setStagedDateTime(Date stagedDateTime) {
+        this.stagedDateTime = stagedDateTime;
+    }
+
     public String getText() {
         return text;
     }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFile.java
1480971e57cdf37b83ddf72ad7727159de218d53
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFragment.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFragment.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragment.java
import java.util.Arrays;
    private SOBIFragmentType type;
    /** A counter used to provide a means of ordering multiple fragments of the same type. */
    private int counter;
    public SOBIFragment(SOBIFile parentSOBIFile, SOBIFragmentType type, String text, int counter) {
        this.type = type;
        this.text = text;
        this.counter = counter;
        return type.equals(SOBIFragmentType.BILL);
    public List<SOBIBlock> getSOBIBlocks() {
            List<String> lines = new ArrayList<>(Arrays.asList(this.text.split("\\r?\\n")));
                        block = new SOBIBlock(fileName, type, lineNo, line);
                        SOBIBlock newBlock = new SOBIBlock(fileName, type, lineNo, line);
                                blocks.add(new SOBIBlock(fileName, type, lineNo,
                                           block.getBillHeader() + SOBILineType.LAW.getTypeCode()));
    @Override
    public String toString() {
        return "SOBIFragment{" + "fragmentType=" + type + ", fileName='" + fileName + '\'' +
                ", parentSOBIFile=" + parentSOBIFile + '}';
    }

    /** --= Functional Getters/Setters --- */

    public Date getPublishedDateTime() {
        return parentSOBIFile.getPublishedDateTime();
    }

    public Date getProcessedDateTime() {
        return parentSOBIFile.getProcessedDateTime();
    }

    public SOBIFragmentType getType() {
        return type;
    public void setFileName(String fileName) {
        this.fileName = fileName;
    public String getText() {
        return text;
    public int getCounter() {
        return counter;
SOBIFile parentSOBIFile  SOBIFragmentType type  String text  int counter
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragment.java
this.type = type;
        this.text = text;
        this.counter = counter;
        return type.equals(SOBIFragmentType.BILL);
    public List<SOBIBlock> getSOBIBlocks() {
            List<String> lines = new ArrayList<>(Arrays.asList(this.text.split("\\r?\\n")));
                        block = new SOBIBlock(fileName, type, lineNo, line);
                        SOBIBlock newBlock = new SOBIBlock(fileName, type, lineNo, line);
                                blocks.add(new SOBIBlock(fileName, type, lineNo,
                                           block.getBillHeader() + SOBILineType.LAW.getTypeCode()));
    @Override
    public String toString() {
        return "SOBIFragment{" + "fragmentType=" + type + ", fileName='" + fileName + '\'' +
                ", parentSOBIFile=" + parentSOBIFile + '
public SOBIFragment(SOBIFile parentSOBIFile, SOBIFragmentType type, String text, int counter)
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragment.java
return parentSOBIFile.getPublishedDateTime();
Date getPublishedDateTime()
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragment.java
return parentSOBIFile.getProcessedDateTime();
Date getProcessedDateTime()
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
    private SOBIFragmentType fragmentType;
    /** The published datetime of the fragment which is the same as that of the parent SOBIFile. */
    private Date publishedDateTime;

    /** The datetime when the fragment was processed. */
    private Date processedDateTime;

    /** Reference to the file containing this fragment */
    private File fragmentFile;
    public SOBIFragment(SOBIFile parentSOBIFile, SOBIFragmentType fragmentType, File fragmentFile) throws IOException {
        this.fragmentType = fragmentType;
        this.fragmentFile = fragmentFile;
        this.fileName = fragmentFile.getName();
        this.publishedDateTime = parentSOBIFile.getPublishedDateTime();
        this.text = FileUtils.readFileToString(fragmentFile);
        return fragmentType.equals(SOBIFragmentType.BILL);
     * @throws IOException if fragment file cannot be opened for reading.
    public List<SOBIBlock> getSOBIBlocks() throws IOException {
            List<String> lines = FileUtils.readLines(fragmentFile);



                        block = new SOBIBlock(fragmentFile, lineNo, line);
                        SOBIBlock newBlock = new SOBIBlock(fragmentFile, lineNo, line);

                                blocks.add(new SOBIBlock(fragmentFile, lineNo, block.getBillHeader()+"B"));

    public SOBIFragmentType getFragmentType() {
        return fragmentType;
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Date getPublishedDateTime() {
        return publishedDateTime;
    public Date getProcessedDateTime() {
        return processedDateTime;
    public void setProcessedDateTime(Date processedDateTime) {
        this.processedDateTime = processedDateTime;
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragment.java
return fragmentType;
    public String getText() {
        return text;
SOBIFragmentType getFragmentType()
String text
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragment.java
this.text = text;
void setText(String text)
@@ -1,10 +1,7 @@
 package gov.nysenate.openleg.model.sobi;
 
-import org.apache.commons.io.FileUtils;
-
-import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -23,32 +20,24 @@ public class SOBIFragment
     private SOBIFile parentSOBIFile;
 
     /** The type of fragment, e.g bill, agenda, etc. */
-    private SOBIFragmentType fragmentType;
+    private SOBIFragmentType type;
 
     /** The file name of the fragment. Serves as a unique identifier for the fragment. */
     private String fileName;
 
-    /** The published datetime of the fragment which is the same as that of the parent SOBIFile. */
-    private Date publishedDateTime;
-
-    /** The datetime when the fragment was processed. */
-    private Date processedDateTime;
-
-    /** Reference to the file containing this fragment */
-    private File fragmentFile;
+    /** A counter used to provide a means of ordering multiple fragments of the same type. */
+    private int counter;
 
     /** The actual text body of the fragment. */
     private String text;
 
     /** --- Constructors --- */
 
-    public SOBIFragment(SOBIFile parentSOBIFile, SOBIFragmentType fragmentType, File fragmentFile) throws IOException {
+    public SOBIFragment(SOBIFile parentSOBIFile, SOBIFragmentType type, String text, int counter) {
         this.parentSOBIFile = parentSOBIFile;
-        this.fragmentType = fragmentType;
-        this.fragmentFile = fragmentFile;
-        this.fileName = fragmentFile.getName();
-        this.publishedDateTime = parentSOBIFile.getPublishedDateTime();
-        this.text = FileUtils.readFileToString(fragmentFile);
+        this.type = type;
+        this.text = text;
+        this.counter = counter;
     }
 
     /** --- Methods --- */
@@ -58,34 +47,30 @@ public SOBIFragment(SOBIFile parentSOBIFile, SOBIFragmentType fragmentType, File
      * @return boolean
      */
     public boolean isBlockFormat() {
-        return fragmentType.equals(SOBIFragmentType.BILL);
+        return type.equals(SOBIFragmentType.BILL);
     }
 
     /**
      * Parses the given SOBI fragment into a list of blocks. Replaces null bytes in each line with spaces to
      * bring them into the proper fixed width formats.
      * <p>See the SOBIBlock class for more details.</p>
      * @return List<SOBIBlock> if fragment type supports blocks, empty list otherwise.
-     * @throws IOException if fragment file cannot be opened for reading.
      */
-    public List<SOBIBlock> getSOBIBlocks() throws IOException {
+    public List<SOBIBlock> getSOBIBlocks() {
         List<SOBIBlock> blocks = new ArrayList<>();
         if (isBlockFormat()) {
             SOBIBlock block = null;
-            List<String> lines = FileUtils.readLines(fragmentFile);
+            List<String> lines = new ArrayList<>(Arrays.asList(this.text.split("\\r?\\n")));
             lines.add(""); // Add a trailing line to end the last block and remove edge cases
-
             for(int lineNo = 0; lineNo < lines.size(); lineNo++) {
                 // Replace NULL bytes with spaces to properly format lines.
                 String line = lines.get(lineNo).replace('\0', ' ');
-
                 // Source file is not assumed to be 100% SOBI so we filter out other lines
                 Matcher headerMatcher = SOBIBlock.blockPattern.matcher(line);
-
                 if (headerMatcher.find()) {
                     if (block == null) {
                         // No active block with a new matching line: create new block
-                        block = new SOBIBlock(fragmentFile, lineNo, line);
+                        block = new SOBIBlock(fileName, type, lineNo, line);
                     }
                     else if (block.getHeader().equals(headerMatcher.group()) && block.isMultiline()) {
                         // active multi-line block with a new matching line: extend block
@@ -95,16 +80,15 @@ else if (block.getHeader().equals(headerMatcher.group()) && block.isMultiline())
                         // active block does not match new line or can't be extended: create new block
                         block.setEndLineNo(lineNo - 1);
                         blocks.add(block);
-                        SOBIBlock newBlock = new SOBIBlock(fragmentFile, lineNo, line);
-
+                        SOBIBlock newBlock = new SOBIBlock(fileName, type, lineNo, line);
                         // Handle certain SOBI grouping edge cases.
                         if (newBlock.getBillHeader().equals(block.getBillHeader())) {
                             // The law code line can be omitted when blank but it always precedes the 'C' line
                             if (newBlock.getType().equals(SOBILineType.SUMMARY) && !block.getType().equals(SOBILineType.LAW)) {
-                                blocks.add(new SOBIBlock(fragmentFile, lineNo, block.getBillHeader()+"B"));
+                                blocks.add(new SOBIBlock(fileName, type, lineNo,
+                                           block.getBillHeader() + SOBILineType.LAW.getTypeCode()));
                             }
                         }
-
                         // Start a new block
                         block = newBlock;
                     }
@@ -120,37 +104,45 @@ else if (block != null) {
         return blocks;
     }
 
+    @Override
+    public String toString() {
+        return "SOBIFragment{" + "fragmentType=" + type + ", fileName='" + fileName + '\'' +
+                ", parentSOBIFile=" + parentSOBIFile + '}';
+    }
+
+    /** --= Functional Getters/Setters --- */
+
+    public Date getPublishedDateTime() {
+        return parentSOBIFile.getPublishedDateTime();
+    }
+
+    public Date getProcessedDateTime() {
+        return parentSOBIFile.getProcessedDateTime();
+    }
+
     /** --- Basic Getters/Setters --- */
 
     public SOBIFile getParentSOBIFile() {
         return parentSOBIFile;
     }
 
-    public SOBIFragmentType getFragmentType() {
-        return fragmentType;
+    public SOBIFragmentType getType() {
+        return type;
     }
 
     public String getFileName() {
         return fileName;
     }
 
-    public String getText() {
-        return text;
-    }
-
-    public void setText(String text) {
-        this.text = text;
-    }
-
-    public Date getPublishedDateTime() {
-        return publishedDateTime;
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
     }
 
-    public Date getProcessedDateTime() {
-        return processedDateTime;
+    public String getText() {
+        return text;
     }
 
-    public void setProcessedDateTime(Date processedDateTime) {
-        this.processedDateTime = processedDateTime;
+    public int getCounter() {
+        return counter;
     }
 }
\ No newline at end of file
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFragment.java
20c37086cf3877f7746a3a058d13a514de698ce4
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFragmentType.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFragmentType.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragmentType.java
 * that a SOBIFragment can represent and regex patterns to extract them.
    BILL            (false, "[0-9]{4}[A-Z][0-9]{5}[ A-Z].+", ""),
    AGENDA          (true, "<senagenda.+", "</senagenda.+"),
    AGENDA_VOTE     (true, "<senagendavote.+", "</senagendavote.+"),
    CALENDAR        (true, "<sencalendar.+", "</sencalendar.+"),
    CALENDAR_ACTIVE (true, "<sencalendaractive.+", "</sencalendaractive.+"),
    COMMITTEE       (true, "<sencommmem.+", "</sencommmem.+"),
    ANNOTATION      (true, "<senannotated.+", "</senannotated.+");

    boolean isXml;
    String startPattern;
    String endPattern;

    SOBIFragmentType(boolean isXml, String startPattern, String endPattern) {
        this.isXml = isXml;
        this.startPattern = startPattern;
        this.endPattern = endPattern;
    }

    public boolean isXml() {
        return isXml;
    }

    public String getStartPattern() {
        return startPattern;
    }

    public String getEndPattern() {
        return endPattern;
    }
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragmentType.java
return isXml;
boolean isXml()
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragmentType.java
return startPattern;
String getStartPattern()
src/main/java/gov/nysenate/openleg/model/sobi/SOBIFragmentType.java
return endPattern;
String getEndPattern()
 * that a SOBIFragment can represent.
    BILL,
    AGENDA,
    AGENDA_VOTE,
    CALENDAR,
    CALENDAR_ACTIVE,
    COMMITTEE,
    ANNOTATION
@@ -3,15 +3,37 @@
 /**
  * SOBIFragments are constructed to delineate a SOBI file into components based on a
  * common entity type. The SOBIFragmentType enum lists all the available entities
- * that a SOBIFragment can represent.
+ * that a SOBIFragment can represent and regex patterns to extract them.
  */
 public enum SOBIFragmentType
 {
-    BILL,
-    AGENDA,
-    AGENDA_VOTE,
-    CALENDAR,
-    CALENDAR_ACTIVE,
-    COMMITTEE,
-    ANNOTATION
+    BILL            (false, "[0-9]{4}[A-Z][0-9]{5}[ A-Z].+", ""),
+    AGENDA          (true, "<senagenda.+", "</senagenda.+"),
+    AGENDA_VOTE     (true, "<senagendavote.+", "</senagendavote.+"),
+    CALENDAR        (true, "<sencalendar.+", "</sencalendar.+"),
+    CALENDAR_ACTIVE (true, "<sencalendaractive.+", "</sencalendaractive.+"),
+    COMMITTEE       (true, "<sencommmem.+", "</sencommmem.+"),
+    ANNOTATION      (true, "<senannotated.+", "</senannotated.+");
+
+    boolean isXml;
+    String startPattern;
+    String endPattern;
+
+    SOBIFragmentType(boolean isXml, String startPattern, String endPattern) {
+        this.isXml = isXml;
+        this.startPattern = startPattern;
+        this.endPattern = endPattern;
+    }
+
+    public boolean isXml() {
+        return isXml;
+    }
+
+    public String getStartPattern() {
+        return startPattern;
+    }
+
+    public String getEndPattern() {
+        return endPattern;
+    }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fmodel%2Fsobi%2FSOBIFragmentType.java
03a8ca4f3481bab809fb4c60b378217b3bf995af
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FDataProcessor.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FDataProcessor.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/processors/DataProcessor.java
import gov.nysenate.openleg.Environment;
import gov.nysenate.openleg.dao.TranscriptDao;
import gov.nysenate.openleg.dao.base.SortOrder;
import gov.nysenate.openleg.dao.sobi.SOBIFileDao;
import gov.nysenate.openleg.dao.sobi.SOBIFragmentDao;
import gov.nysenate.openleg.dao.sobi.SqlSOBIFragmentDao;
import gov.nysenate.openleg.model.sobi.SOBIFile;
import gov.nysenate.openleg.model.sobi.SOBIFragment;
import gov.nysenate.openleg.model.sobi.SOBIFragmentType;
import gov.nysenate.openleg.processors.sobi.*;
import static gov.nysenate.openleg.util.FileHelper.*;

    private static Logger logger = Logger.getLogger(DataProcessor.class);
    /** The environment where the data is to be processed. */
    private Environment environment;
    private Map<SOBIFragmentType, SOBIProcessor> processorMap;
    /** -- DAO instances --- */

    private SOBIFileDao sobiFileDao;
    private SOBIFragmentDao sobiFragmentDao;
    private TranscriptDao transcriptDao;

    /** --- Constructors --- */

    public DataProcessor(Environment environment) {
        this.environment = environment;

        SqlSOBIFragmentDao fragmentDao = new SqlSOBIFragmentDao(environment);
        this.sobiFileDao = fragmentDao;
        this.sobiFragmentDao = fragmentDao;

        this.processorMap = new HashMap<>();
        this.processorMap.put(SOBIFragmentType.BILL, new BillProcessor(environment));
        this.processorMap.put(SOBIFragmentType.AGENDA, new AgendaProcessor());
        this.processorMap.put(SOBIFragmentType.AGENDA_VOTE, new AgendaVoteProcessor());
        this.processorMap.put(SOBIFragmentType.CALENDAR, new CalendarProcessor());
        this.processorMap.put(SOBIFragmentType.CALENDAR_ACTIVE, new CalendarActiveListProcessor());
        this.processorMap.put(SOBIFragmentType.COMMITTEE, new CommitteeProcessor());
    /** --- Processing methods --- */

     * @throws ParseException
    public void stage(File sourceDir, File workDir) throws IOException, ParseException {
        // Prepares incoming SOBI files for processing
        sobiFileDao.stageSOBIFiles(true);
        // TODO: Stage all hearings to the work directory.
        // TODO: Stage all transcripts
     *
     * @throws IOException, ParseException
    public void collate(File workDir) throws IOException, ParseException {
        for (SOBIFile sobiFile : sobiFileDao.getPendingSOBIFiles(SortOrder.ASC)) {
            StringBuilder billBuffer = new StringBuilder();
            int fragmentCounter = 1;
            List<SOBIFragment> sobiFragments = new ArrayList<>();
            List<String> lines = Arrays.asList(sobiFile.getText().split("\\r?\\n"));
            Iterator<String> lineIterator = lines.iterator();
            // Construct fragments from SOBI text
            while (lineIterator.hasNext()) {
                String line = lineIterator.next();
                SOBIFragmentType fragmentType = getFragmentTypeFromLine(line);
                if (fragmentType != null) {
                    // Bill portions are appended into a single buffer
                    if (fragmentType.equals(SOBIFragmentType.BILL)) {
                        if (line.charAt(11) == 'M') {
                            // Memos are latin1 encoding
                            line = new String(line.getBytes(encoding), "latin1");
                        }
                        line = line.replace((char)193, '°');
                        billBuffer.append(line).append("\n");
                    }
                    // Other fragment types are in XML format
                    else {
                        String xmlText = extractXmlText(fragmentType, line, lineIterator);
                        SOBIFragment fragment = new SOBIFragment(sobiFile, fragmentType, xmlText, fragmentCounter++);
                        sobiFragments.add(fragment);
            if (billBuffer.length() > 0) {
                SOBIFragment billFragment = new SOBIFragment(sobiFile, SOBIFragmentType.BILL, billBuffer.toString(), 1);
                sobiFragments.add(billFragment);
            }
            // Persist the fragments
            sobiFragmentDao.saveSOBIFragments(sobiFragments);
    public void ingest(File workingDir, Storage storage) throws IOException {
        // Process SOBI files
        for (SOBIFile sobiFile : sobiFileDao.getPendingSOBIFiles(SortOrder.ASC)) {
            List<SOBIFragment> fragments = sobiFragmentDao.getSOBIFragments(sobiFile, SortOrder.ASC);
            for (SOBIFragment fragment : fragments) {
                if (processorMap.containsKey(fragment.getType())) {
                    processorMap.get(fragment.getType()).process(fragment);
                else {
                    logger.warn("No processors have been registered to handle: " + fragment);
            sobiFile.incrementProcessedCount();
            sobiFile.setProcessedDateTime(new Date());
            sobiFile.setPendingProcessing(false);
            sobiFileDao.updateSOBIFile(sobiFile);

        // TODO: Process Transcripts / Public Hearings
        // TODO: Handle CMS.TEXT (Rules file)
    public void push(Storage storage, List<Entry<String, Change>> changes, List<ServiceBase> services) {
    public void archive(File workingDir, File archiveDir) throws IOException {
    /** --- Internal Methods --- */

    /**
     * Check the given SOBI line to determine if it matches the start of a SOBI Fragment type.
     * @param line String
     * @return SOBIFragmentType or null if no match
     */
    protected SOBIFragmentType getFragmentTypeFromLine(String line) {
        for (SOBIFragmentType fragmentType : SOBIFragmentType.values()) {
            if (line.matches(fragmentType.getStartPattern())) {
                return fragmentType;
            }
        return null;
    protected void archiveFiles(File sourceDir, File destDir, String subFolder) throws IOException {
     * Extracts a well formed XML document from the lines and writes it to the given
     * we'll get malformed XML docs. TODO: EDIT THIS DOC
     *
     * @param fragmentType SOBIFragmentType
     * @param line String - The starting line of the document
     * @param iterator Iterator<String>
     * @return String - The resulting XML string.
    protected String extractXmlText(SOBIFragmentType fragmentType, String line, Iterator<String> iterator) throws IOException {
        String endPattern = fragmentType.getEndPattern();

        String in = null;
        while (iterator.hasNext()) {
            in = iterator.next();
            if (in.matches(endPattern)) {
            }
        // TODO: Figure out all this matcher magic. How does it work?
        return data;
Environment environment
src/main/java/gov/nysenate/openleg/processors/DataProcessor.java
this.environment = environment;

        SqlSOBIFragmentDao fragmentDao = new SqlSOBIFragmentDao(environment);
        this.sobiFileDao = fragmentDao;
        this.sobiFragmentDao = fragmentDao;

        this.processorMap = new HashMap<>();
        this.processorMap.put(SOBIFragmentType.BILL, new BillProcessor(environment));
        this.processorMap.put(SOBIFragmentType.AGENDA, new AgendaProcessor());
        this.processorMap.put(SOBIFragmentType.AGENDA_VOTE, new AgendaVoteProcessor());
        this.processorMap.put(SOBIFragmentType.CALENDAR, new CalendarProcessor());
        this.processorMap.put(SOBIFragmentType.CALENDAR_ACTIVE, new CalendarActiveListProcessor());
        this.processorMap.put(SOBIFragmentType.COMMITTEE, new CommitteeProcessor());
    /** --- Processing methods --- */

     * @throws ParseException
    public void stage(File sourceDir, File workDir) throws IOException, ParseException {
        // Prepares incoming SOBI files for processing
        sobiFileDao.stageSOBIFiles(true);
        // TODO: Stage all hearings to the work directory.
        // TODO: Stage all transcripts
     *
     * @throws IOException, ParseException
    public void collate(File workDir) throws IOException, ParseException {
        for (SOBIFile sobiFile : sobiFileDao.getPendingSOBIFiles(SortOrder.ASC)) {
            StringBuilder billBuffer = new StringBuilder();
            int fragmentCounter = 1;
            List<SOBIFragment> sobiFragments = new ArrayList<>();
            List<String> lines = Arrays.asList(sobiFile.getText().split("\\r?\\n"));
            Iterator<String> lineIterator = lines.iterator();
            // Construct fragments from SOBI text
            while (lineIterator.hasNext()) {
                String line = lineIterator.next();
                SOBIFragmentType fragmentType = getFragmentTypeFromLine(line);
                if (fragmentType != null) {
                    // Bill portions are appended into a single buffer
                    if (fragmentType.equals(SOBIFragmentType.BILL)) {
                        if (line.charAt(11) == 'M') {
                            // Memos are latin1 encoding
                            line = new String(line.getBytes(encoding), "latin1");
public DataProcessor(Environment environment)
Storage storage  List<Entry<String  Change>> changes  List<ServiceBase> services
src/main/java/gov/nysenate/openleg/processors/DataProcessor.java
public void archive(File workingDir, File archiveDir) throws IOException {
    /** --- Internal Methods --- */

    /**
     * Check the given SOBI line to determine if it matches the start of a SOBI Fragment type.
     * @param line String
     * @return SOBIFragmentType or null if no match
     */
    protected SOBIFragmentType getFragmentTypeFromLine(String line) {
        for (SOBIFragmentType fragmentType : SOBIFragmentType.values()) {
            if (line.matches(fragmentType.getStartPattern())) {
                return fragmentType;
void push(Storage storage, List<Entry<String, Change>> changes, List<ServiceBase> services)
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.*;
import org.xml.sax.SAXException;
import javax.xml.xpath.XPathExpressionException;
import java.io.BufferedReader;
import java.io.StringReader;
 *
 * @author GraylinKim
    Logger logger = Logger.getLogger(DataProcessor.class);
    /**
     * SOBI.DYYMMDD.THHMMSS.TXT files are (mostly) in a CP850 or similar encoding. This
     * was determined from the byte mapping of paragraph/section characters to 244/245.
     *
     * This can't be 100% correct though because the degree symbol must be 193 in the
     * correct code set. See SOBI.D120612.T125850.TXT.
     */
    /**
     * Any directory that we attempt to list files from should exist. If it doesn't then
     * create them. This makes the processes robust against incomplete environment setups.
     *
     * @param directory - The directory to list files from (and create if necessary)
     * @param extensions - A list of extensions to grab. null for all extensions.
     * @param recursive - true when you want to list files recursively.
     * @return A collection of matching filenames
     * @throws IOException
     */
    public Collection<File> safeListFiles(File directory, String[] extensions, boolean recursive) throws IOException
    {
        FileUtils.forceMkdir(directory);
        return FileUtils.listFiles(directory, extensions, recursive);
    }
    /**
     * Any directory that we attempt to list files from should exist. If it doesn't then
     * create them. This overload can be used to exclude certain directories if recursive is true.
     *
     * @param directory - The directory to list files from (and create if necessary)
     * @param recursive - true when you want to list files recursively.
     * @param excludeDirs - Array of directory names that should be excluded from the match.
     *                      Set to null if all directories should be matched.
     * @return A collection of matching filenames
     * @throws IOException
     */
    public Collection<File> safeListFiles(File directory, boolean recursive, String[] excludeDirs) throws IOException
    {
        FileUtils.forceMkdir(directory);
        IOFileFilter dirFileFilter;
        if (excludeDirs != null && excludeDirs.length > 0) {
            List<IOFileFilter> excludeDirFilters = new ArrayList<>();
            for (String excludeDir : excludeDirs) {
                excludeDirFilters.add(FileFilterUtils.nameFileFilter(excludeDir));
            }
            dirFileFilter = FileFilterUtils.notFileFilter(new OrFileFilter(excludeDirFilters));
        }
        else {
            dirFileFilter = (recursive) ? TrueFileFilter.TRUE : FalseFileFilter.FALSE;
        }
        return FileUtils.listFiles(directory, TrueFileFilter.TRUE, dirFileFilter);
    }
    /**
     * Create the specified folder if necessary and return a File handle to it.
     *
     * @param parent - The parent directory for this folder
     * @param folderName - The name of the directory to retrieve
     * @return a File handle to the requested folder.
     * @throws IOException
     */
    public File safeGetFolder(File parent, String folderName) throws IOException
    {
        File directory = new File(parent, folderName);
        FileUtils.forceMkdir(directory);
        return directory;
    public void stage(File sourceDir, File workDir) throws IOException
    {
        // Everything else on this level should be a SOBI file
        File sobiDir = safeGetFolder(workDir, "sobis");
        for (File sobiFile : safeListFiles(sourceDir, null, false)) {
            moveFileToDirectory(sobiFile, sobiDir, true);
        }
        File hearingDir = safeGetFolder(workDir, "hearings");
        for (File hearingFile : safeListFiles(safeGetFolder(sourceDir, "hearings"), null, false)) {
            moveFileToDirectory(hearingFile, hearingDir, true);
        }
        File transcriptDir = safeGetFolder(workDir, "transcripts");
        for (File transcriptFile : safeListFiles(safeGetFolder(sourceDir, "transcripts"), null, false)) {
            moveFileToDirectory(transcriptFile, transcriptDir, true);
        }
     * Looks for SOBI files in the sobis directory and extracts the various sub-documents
     * into properly encoded UTF-8 files in their respective directories for later
     * processing.
     * @throws IOException
    public void collate(File workDir) throws IOException
    {
        // Folders for our extracted sub documents. Extracting them and writing them to file
        // provides an easily inspectable record of how this step went.
        File billDir = safeGetFolder(workDir, "bills");
        File agendaDir = safeGetFolder(workDir, "agendas");
        File agendaVoteDir = safeGetFolder(workDir, "agendavotes");
        File calendarDir = safeGetFolder(workDir, "calendars");
        File activeListDir = safeGetFolder(workDir, "calendaractives");
        File committeeDir = safeGetFolder(workDir, "committees");
        File annotationDir = safeGetFolder(workDir, "annotations");

        for (File sobiFile : safeListFiles(safeGetFolder(workDir, "sobis"), null, true)) {
            String line = null;
            int fileCounter = 1;
            StringBuffer billBuffer = new StringBuffer();
            BufferedReader br = new BufferedReader(new StringReader(FileUtils.readFileToString(sobiFile, encoding)));

            while((line = br.readLine()) != null) {
                if(line.matches("<sencalendaractive.+")) {
                    // Extract calendars and active lists
                    File activeListFile = new File(activeListDir, sobiFile.getName()+"-calendaractive-"+(fileCounter++)+".xml");
                    logger.info("Extracting calendaractive: "+activeListFile);
                    extractXml("</sencalendaractive.+", line, br, activeListFile);
                }
                else if(line.matches("<sencalendar.+")) {
                    // Extract calendars and active lists
                    File calendarFile = new File(calendarDir, sobiFile.getName()+"-calendar-"+(fileCounter++)+".xml");
                    logger.info("Extracting calendar: "+calendarFile);
                    extractXml("</sencalendar.+", line, br, calendarFile);
                }
                else if(line.matches("<sencommmem.+")) {
                    File committeeFile = new File(committeeDir, sobiFile.getName()+"-committee-"+(fileCounter++)+".xml");
                    logger.info("Extracting commitee: "+committeeFile);
                    extractXml("</sencommmem.+", line, br, committeeFile);
                }
                else if(line.matches("<senagendavote.+")) {
                    // Extract agendas and corresponding votes
                    File agendaVoteFile = new File(agendaVoteDir, sobiFile.getName()+"-agendavote-"+(fileCounter++)+".xml");
                    logger.info("Extracting agendavote: "+agendaVoteFile);
                    extractXml("</senagendavote.+", line, br, agendaVoteFile);
                }
                else if(line.matches("<senagenda.+")) {
                    // Extract agendas and corresponding votes
                    File agendaFile = new File(agendaDir, sobiFile.getName()+"-agenda-"+(fileCounter++)+".xml");
                    logger.info("Extracting agenda: "+agendaFile);
                    extractXml("</senagenda.+", line, br, agendaFile);
                }
                else if(line.matches("<senannotated.+")) {
                    // Extract unused annotation files. We stopped getting these in 2009.
                    File annotationFile = new File(annotationDir, sobiFile.getName()+"-annotation-"+(fileCounter++)+".xml");
                    logger.info("Extracting annotation: "+annotationFile);
                    extractXml("</senannotated.+", line, br, annotationFile);
                }
                else if(line.matches("[0-9]{4}[A-Z][0-9]{5}[ A-Z].+")) {
                    if (line.charAt(11) == 'M') {
                        // Memos are latin1 encoding
                        line = new String(line.getBytes(encoding), "latin1");

                    line = line.replace((char)193, '°');
                    billBuffer.append(line).append("\n");

            br.close();
            logger.info("Writing bill sobi to "+sobiFile);
            File billFile = new File(billDir, sobiFile.getName()+"-bill-"+(fileCounter++)+".sobi");
            FileUtils.write(billFile, billBuffer.toString());
    public void ingest(File workingDir, Storage storage) throws IOException, ParserConfigurationException
    {
        BillProcessor billProcessor = new BillProcessor();
        SenagendaProcessor agendaProcessor = new SenagendaProcessor();
        SencalendarProcessor calendarProcessor = new SencalendarProcessor();
        TranscriptProcessor transcriptProcessor = new TranscriptProcessor();

        for (File file : getSortedFiles(workingDir, true, new String[] {"sobis"})) {
            try {
                logger.info("Working on: "+file);
                String type = file.getParentFile().getName();
                if (type.equals("bills")) {
                    billProcessor.process(file, storage);
                } else if (type.equals("calendars")) {
                    calendarProcessor.processSencalendar(file, storage);
                } else if (type.equals("calendaractives")) {
                    calendarProcessor.processSencalendarActive(file, storage);
                } else if (type.equals("agendas")) {
                    agendaProcessor.processSenagenda(file, storage);
                } else if (type.equals("agendavotes")) {
                    agendaProcessor.processSenagendaVote(file, storage);
                } else if (type.equals("annotations")) {
                    continue; // we don't process or receive these anymore
                } else if (type.equals("transcripts")) {
                    transcriptProcessor.process(file, storage);
                } else if (type.equals("hearings")) {
                    continue; // we don't process or receive these yet.
                } else if (type.equals("committees")) {
                    continue; // We don't process
                } else if (file.getName().equals("CMS.TEXT")) {
                    // The rules don't really need processing, just put them somewhere for later
                    FileUtils.copyFileToDirectory(file, storage.getStorageDir());

                // To avoid memory issues, occasionally flush changes to file-system and truncate memory
                if (storage.memory.size() > 4000) {
                    storage.flush();
                    storage.clear();
            catch (IOException e) {
                logger.error("IO issue with "+file.getName(), e);
            }
            catch (XPathExpressionException e) {
                logger.error("Xpath issue with "+file.getName(), e);
            }
            catch (SAXException e) {
                logger.error("SAX issue with "+file.getName(), e);
            }
            catch (ParseException e) {
                logger.error("XML issue with "+file.getName(), e);
            }
        storage.flush();
    public void push(Storage storage, List<Entry<String, Change>> changes, List<ServiceBase> services)
    {
    public void archive(File workingDir, File archiveDir) throws IOException
    {
    public void moveFileToDirectory(File file, File directory, boolean createDirectory) throws IOException
    {
        File newFile = new File(directory, file.getName());
        if (newFile.exists()) {
            newFile.delete();
        FileUtils.moveFileToDirectory(file, directory, true);
    protected void archiveFiles(File sourceDir, File destDir, String subFolder) throws IOException
    {
     * Returns a collection of files sorted by file name (not file path!)
     *
     * @param directory - The directory to get files from.
     * @param recursive - true to retrieve files from sub-directories.
     * @return
     * @throws IOException
     */
    protected Collection<File> getSortedFiles(File directory, boolean recursive, String[] excludeDirs) throws IOException {
        Collection<File> files = safeListFiles(directory, recursive, excludeDirs);
        Collections.sort((List<File>)files, new Comparator<File>(){
            public int compare(File a, File b) {
                return a.getName().compareTo(b.getName());
            }
        });
        return files;
    }

    /**
     * Extracts a well formed XML document from a BufferedReader and writes it to the given
     * we'll get malformed XML docs.
     * @param escape - Regex matching the escape sequence for the XML document. e.g. {@literal </senagenda.+}
     * @param line - The starting line of the document
     * @param br - The buffered reader used to retrieve additional lines from the document
     * @param file - The file that the resulting XML document should be written to.
    protected void extractXml(String escape, String line, BufferedReader br, File file) throws IOException
    {
        String in;
        while((in = br.readLine()) != null) {
            if(in.matches(escape))
        // TODO: Figure out all this matcher magic. How does it work? What the hell is it doing?
        FileUtils.write(file, data);
Storage storage  List<Entry<String  Change>> changes  List<ServiceBase> services
src/main/java/gov/nysenate/openleg/processors/DataProcessor.java
public void archive(File workingDir, File archiveDir) throws IOException
    {
    public void moveFileToDirectory(File file, File directory, boolean createDirectory) throws IOException
    {
        File newFile = new File(directory, file.getName());
        if (newFile.exists()) {
            newFile.delete();
        FileUtils.moveFileToDirectory(file, directory, true);
    protected void archiveFiles(File sourceDir, File destDir, String subFolder) throws IOException
    {
     * Returns a collection of files sorted by file name (not file path!)
     *
     * @param directory - The directory to get files from.
     * @param recursive - true to retrieve files from sub-directories.
     * @return
     * @throws IOException
     */
    protected Collection<File> getSortedFiles(File directory, boolean recursive, String[] excludeDirs) throws IOException {
        Collection<File> files = safeListFiles(directory, recursive, excludeDirs);
        Collections.sort((List<File>)files, new Comparator<File>(){
            public int compare(File a, File b) {
                return a.getName().compareTo(b.getName());
void push(Storage storage, List<Entry<String, Change>> changes, List<ServiceBase> services)
@@ -1,26 +1,32 @@
 package gov.nysenate.openleg.processors;
 
+import gov.nysenate.openleg.Environment;
+import gov.nysenate.openleg.dao.TranscriptDao;
+import gov.nysenate.openleg.dao.base.SortOrder;
+import gov.nysenate.openleg.dao.sobi.SOBIFileDao;
+import gov.nysenate.openleg.dao.sobi.SOBIFragmentDao;
+import gov.nysenate.openleg.dao.sobi.SqlSOBIFragmentDao;
 import gov.nysenate.openleg.model.Change;
+import gov.nysenate.openleg.model.sobi.SOBIFile;
+import gov.nysenate.openleg.model.sobi.SOBIFragment;
+import gov.nysenate.openleg.model.sobi.SOBIFragmentType;
+import gov.nysenate.openleg.processors.sobi.*;
 import gov.nysenate.openleg.services.ServiceBase;
 import gov.nysenate.openleg.util.Storage;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.filefilter.*;
 import org.apache.log4j.Logger;
-import org.xml.sax.SAXException;
 
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPathExpressionException;
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
-import java.io.StringReader;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.Map.Entry;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static gov.nysenate.openleg.util.FileHelper.*;
+
 /**
  * Contains an implementation for each step of the Open Legislation data processing pipeline.
  *
@@ -51,190 +57,108 @@
  *      Archives all working files for reference when going through logs in the future.
  *   </li>
  * </ul>
- *
- * @author GraylinKim
  */
 public class DataProcessor
 {
-    Logger logger = Logger.getLogger(DataProcessor.class);
+    private static Logger logger = Logger.getLogger(DataProcessor.class);
 
-    /**
-     * SOBI.DYYMMDD.THHMMSS.TXT files are (mostly) in a CP850 or similar encoding. This
-     * was determined from the byte mapping of paragraph/section characters to 244/245.
-     *
-     * This can't be 100% correct though because the degree symbol must be 193 in the
-     * correct code set. See SOBI.D120612.T125850.TXT.
-     */
     protected static String encoding = "CP850";
 
-    /**
-     * Any directory that we attempt to list files from should exist. If it doesn't then
-     * create them. This makes the processes robust against incomplete environment setups.
-     *
-     * @param directory - The directory to list files from (and create if necessary)
-     * @param extensions - A list of extensions to grab. null for all extensions.
-     * @param recursive - true when you want to list files recursively.
-     * @return A collection of matching filenames
-     * @throws IOException
-     */
-    public Collection<File> safeListFiles(File directory, String[] extensions, boolean recursive) throws IOException
-    {
-        FileUtils.forceMkdir(directory);
-        return FileUtils.listFiles(directory, extensions, recursive);
-    }
+    /** The environment where the data is to be processed. */
+    private Environment environment;
 
-    /**
-     * Any directory that we attempt to list files from should exist. If it doesn't then
-     * create them. This overload can be used to exclude certain directories if recursive is true.
-     *
-     * @param directory - The directory to list files from (and create if necessary)
-     * @param recursive - true when you want to list files recursively.
-     * @param excludeDirs - Array of directory names that should be excluded from the match.
-     *                      Set to null if all directories should be matched.
-     * @return A collection of matching filenames
-     * @throws IOException
-     */
-    public Collection<File> safeListFiles(File directory, boolean recursive, String[] excludeDirs) throws IOException
-    {
-        FileUtils.forceMkdir(directory);
-        IOFileFilter dirFileFilter;
-        if (excludeDirs != null && excludeDirs.length > 0) {
-            List<IOFileFilter> excludeDirFilters = new ArrayList<>();
-            for (String excludeDir : excludeDirs) {
-                excludeDirFilters.add(FileFilterUtils.nameFileFilter(excludeDir));
-            }
-            dirFileFilter = FileFilterUtils.notFileFilter(new OrFileFilter(excludeDirFilters));
-        }
-        else {
-            dirFileFilter = (recursive) ? TrueFileFilter.TRUE : FalseFileFilter.FALSE;
-        }
-        return FileUtils.listFiles(directory, TrueFileFilter.TRUE, dirFileFilter);
-    }
+    private Map<SOBIFragmentType, SOBIProcessor> processorMap;
 
-    /**
-     * Create the specified folder if necessary and return a File handle to it.
-     *
-     * @param parent - The parent directory for this folder
-     * @param folderName - The name of the directory to retrieve
-     * @return a File handle to the requested folder.
-     * @throws IOException
-     */
-    public File safeGetFolder(File parent, String folderName) throws IOException
-    {
-        File directory = new File(parent, folderName);
-        FileUtils.forceMkdir(directory);
-        return directory;
+    /** -- DAO instances --- */
+
+    private SOBIFileDao sobiFileDao;
+    private SOBIFragmentDao sobiFragmentDao;
+    private TranscriptDao transcriptDao;
+
+    /** --- Constructors --- */
+
+    public DataProcessor(Environment environment) {
+        this.environment = environment;
+
+        SqlSOBIFragmentDao fragmentDao = new SqlSOBIFragmentDao(environment);
+        this.sobiFileDao = fragmentDao;
+        this.sobiFragmentDao = fragmentDao;
+
+        this.processorMap = new HashMap<>();
+        this.processorMap.put(SOBIFragmentType.BILL, new BillProcessor(environment));
+        this.processorMap.put(SOBIFragmentType.AGENDA, new AgendaProcessor());
+        this.processorMap.put(SOBIFragmentType.AGENDA_VOTE, new AgendaVoteProcessor());
+        this.processorMap.put(SOBIFragmentType.CALENDAR, new CalendarProcessor());
+        this.processorMap.put(SOBIFragmentType.CALENDAR_ACTIVE, new CalendarActiveListProcessor());
+        this.processorMap.put(SOBIFragmentType.COMMITTEE, new CommitteeProcessor());
     }
 
+    /** --- Processing methods --- */
+
     /**
      * Stages CMS.TEXT, transcripts/, hearings/ and SOBI format files from the source
      * directory into their respective destinations subfolders in the work directory.
      *
      * @param sourceDir - The directory to pull files from
      * @param workDir - The directory to stage the files into
      * @throws IOException
+     * @throws ParseException
      */
-    public void stage(File sourceDir, File workDir) throws IOException
-    {
+    public void stage(File sourceDir, File workDir) throws IOException, ParseException {
         File rulesFile = new File(sourceDir,"CMS.TEXT");
         if (rulesFile.exists()) {
             moveFileToDirectory(rulesFile, workDir, true);
         }
 
-        // Everything else on this level should be a SOBI file
-        File sobiDir = safeGetFolder(workDir, "sobis");
-        for (File sobiFile : safeListFiles(sourceDir, null, false)) {
-            moveFileToDirectory(sobiFile, sobiDir, true);
-        }
+        // Prepares incoming SOBI files for processing
+        sobiFileDao.stageSOBIFiles(true);
 
-        File hearingDir = safeGetFolder(workDir, "hearings");
-        for (File hearingFile : safeListFiles(safeGetFolder(sourceDir, "hearings"), null, false)) {
-            moveFileToDirectory(hearingFile, hearingDir, true);
-        }
+        // TODO: Stage all hearings to the work directory.
 
-        File transcriptDir = safeGetFolder(workDir, "transcripts");
-        for (File transcriptFile : safeListFiles(safeGetFolder(sourceDir, "transcripts"), null, false)) {
-            moveFileToDirectory(transcriptFile, transcriptDir, true);
-        }
+        // TODO: Stage all transcripts
     }
 
     /**
-     * Looks for SOBI files in the sobis directory and extracts the various sub-documents
-     * into properly encoded UTF-8 files in their respective directories for later
-     * processing.
+     *
      *
      * @param workDir - The working directory with files to collate
-     * @throws IOException
+     * @throws IOException, ParseException
      */
-    public void collate(File workDir) throws IOException
-    {
-        // Folders for our extracted sub documents. Extracting them and writing them to file
-        // provides an easily inspectable record of how this step went.
-        File billDir = safeGetFolder(workDir, "bills");
-        File agendaDir = safeGetFolder(workDir, "agendas");
-        File agendaVoteDir = safeGetFolder(workDir, "agendavotes");
-        File calendarDir = safeGetFolder(workDir, "calendars");
-        File activeListDir = safeGetFolder(workDir, "calendaractives");
-        File committeeDir = safeGetFolder(workDir, "committees");
-        File annotationDir = safeGetFolder(workDir, "annotations");
-
-        for (File sobiFile : safeListFiles(safeGetFolder(workDir, "sobis"), null, true)) {
-            String line = null;
-            int fileCounter = 1;
-            StringBuffer billBuffer = new StringBuffer();
-            BufferedReader br = new BufferedReader(new StringReader(FileUtils.readFileToString(sobiFile, encoding)));
-
-            while((line = br.readLine()) != null) {
-                if(line.matches("<sencalendaractive.+")) {
-                    // Extract calendars and active lists
-                    File activeListFile = new File(activeListDir, sobiFile.getName()+"-calendaractive-"+(fileCounter++)+".xml");
-                    logger.info("Extracting calendaractive: "+activeListFile);
-                    extractXml("</sencalendaractive.+", line, br, activeListFile);
-                }
-                else if(line.matches("<sencalendar.+")) {
-                    // Extract calendars and active lists
-                    File calendarFile = new File(calendarDir, sobiFile.getName()+"-calendar-"+(fileCounter++)+".xml");
-                    logger.info("Extracting calendar: "+calendarFile);
-                    extractXml("</sencalendar.+", line, br, calendarFile);
-                }
-                else if(line.matches("<sencommmem.+")) {
-                    File committeeFile = new File(committeeDir, sobiFile.getName()+"-committee-"+(fileCounter++)+".xml");
-                    logger.info("Extracting commitee: "+committeeFile);
-                    extractXml("</sencommmem.+", line, br, committeeFile);
-                }
-                else if(line.matches("<senagendavote.+")) {
-                    // Extract agendas and corresponding votes
-                    File agendaVoteFile = new File(agendaVoteDir, sobiFile.getName()+"-agendavote-"+(fileCounter++)+".xml");
-                    logger.info("Extracting agendavote: "+agendaVoteFile);
-                    extractXml("</senagendavote.+", line, br, agendaVoteFile);
-                }
-                else if(line.matches("<senagenda.+")) {
-                    // Extract agendas and corresponding votes
-                    File agendaFile = new File(agendaDir, sobiFile.getName()+"-agenda-"+(fileCounter++)+".xml");
-                    logger.info("Extracting agenda: "+agendaFile);
-                    extractXml("</senagenda.+", line, br, agendaFile);
-                }
-                else if(line.matches("<senannotated.+")) {
-                    // Extract unused annotation files. We stopped getting these in 2009.
-                    File annotationFile = new File(annotationDir, sobiFile.getName()+"-annotation-"+(fileCounter++)+".xml");
-                    logger.info("Extracting annotation: "+annotationFile);
-                    extractXml("</senannotated.+", line, br, annotationFile);
-                }
-                else if(line.matches("[0-9]{4}[A-Z][0-9]{5}[ A-Z].+")) {
-                    if (line.charAt(11) == 'M') {
-                        // Memos are latin1 encoding
-                        line = new String(line.getBytes(encoding), "latin1");
+    public void collate(File workDir) throws IOException, ParseException {
+        for (SOBIFile sobiFile : sobiFileDao.getPendingSOBIFiles(SortOrder.ASC)) {
+            StringBuilder billBuffer = new StringBuilder();
+            int fragmentCounter = 1;
+            List<SOBIFragment> sobiFragments = new ArrayList<>();
+            List<String> lines = Arrays.asList(sobiFile.getText().split("\\r?\\n"));
+            Iterator<String> lineIterator = lines.iterator();
+            // Construct fragments from SOBI text
+            while (lineIterator.hasNext()) {
+                String line = lineIterator.next();
+                SOBIFragmentType fragmentType = getFragmentTypeFromLine(line);
+                if (fragmentType != null) {
+                    // Bill portions are appended into a single buffer
+                    if (fragmentType.equals(SOBIFragmentType.BILL)) {
+                        if (line.charAt(11) == 'M') {
+                            // Memos are latin1 encoding
+                            line = new String(line.getBytes(encoding), "latin1");
+                        }
+                        line = line.replace((char)193, '°');
+                        billBuffer.append(line).append("\n");
+                    }
+                    // Other fragment types are in XML format
+                    else {
+                        String xmlText = extractXmlText(fragmentType, line, lineIterator);
+                        SOBIFragment fragment = new SOBIFragment(sobiFile, fragmentType, xmlText, fragmentCounter++);
+                        sobiFragments.add(fragment);
                     }
-
-                    line = line.replace((char)193, '°');
-                    billBuffer.append(line).append("\n");
                 }
             }
-
-            br.close();
-            logger.info("Writing bill sobi to "+sobiFile);
-            File billFile = new File(billDir, sobiFile.getName()+"-bill-"+(fileCounter++)+".sobi");
-            FileUtils.write(billFile, billBuffer.toString());
+            if (billBuffer.length() > 0) {
+                SOBIFragment billFragment = new SOBIFragment(sobiFile, SOBIFragmentType.BILL, billBuffer.toString(), 1);
+                sobiFragments.add(billFragment);
+            }
+            // Persist the fragments
+            sobiFragmentDao.saveSOBIFragments(sobiFragments);
         }
     }
 
@@ -248,60 +172,26 @@ else if(line.matches("[0-9]{4}[A-Z][0-9]{5}[ A-Z].+")) {
      * @throws IOException
      * @throws ParserConfigurationException
      */
-    public void ingest(File workingDir, Storage storage) throws IOException, ParserConfigurationException
-    {
-        BillProcessor billProcessor = new BillProcessor();
-        SenagendaProcessor agendaProcessor = new SenagendaProcessor();
-        SencalendarProcessor calendarProcessor = new SencalendarProcessor();
-        TranscriptProcessor transcriptProcessor = new TranscriptProcessor();
-
-        for (File file : getSortedFiles(workingDir, true, new String[] {"sobis"})) {
-            try {
-                logger.info("Working on: "+file);
-                String type = file.getParentFile().getName();
-                if (type.equals("bills")) {
-                    billProcessor.process(file, storage);
-                } else if (type.equals("calendars")) {
-                    calendarProcessor.processSencalendar(file, storage);
-                } else if (type.equals("calendaractives")) {
-                    calendarProcessor.processSencalendarActive(file, storage);
-                } else if (type.equals("agendas")) {
-                    agendaProcessor.processSenagenda(file, storage);
-                } else if (type.equals("agendavotes")) {
-                    agendaProcessor.processSenagendaVote(file, storage);
-                } else if (type.equals("annotations")) {
-                    continue; // we don't process or receive these anymore
-                } else if (type.equals("transcripts")) {
-                    transcriptProcessor.process(file, storage);
-                } else if (type.equals("hearings")) {
-                    continue; // we don't process or receive these yet.
-                } else if (type.equals("committees")) {
-                    continue; // We don't process
-                } else if (file.getName().equals("CMS.TEXT")) {
-                    // The rules don't really need processing, just put them somewhere for later
-                    FileUtils.copyFileToDirectory(file, storage.getStorageDir());
+    public void ingest(File workingDir, Storage storage) throws IOException {
+        // Process SOBI files
+        for (SOBIFile sobiFile : sobiFileDao.getPendingSOBIFiles(SortOrder.ASC)) {
+            List<SOBIFragment> fragments = sobiFragmentDao.getSOBIFragments(sobiFile, SortOrder.ASC);
+            for (SOBIFragment fragment : fragments) {
+                if (processorMap.containsKey(fragment.getType())) {
+                    processorMap.get(fragment.getType()).process(fragment);
                 }
-
-                // To avoid memory issues, occasionally flush changes to file-system and truncate memory
-                if (storage.memory.size() > 4000) {
-                    storage.flush();
-                    storage.clear();
+                else {
+                    logger.warn("No processors have been registered to handle: " + fragment);
                 }
             }
-            catch (IOException e) {
-                logger.error("IO issue with "+file.getName(), e);
-            }
-            catch (XPathExpressionException e) {
-                logger.error("Xpath issue with "+file.getName(), e);
-            }
-            catch (SAXException e) {
-                logger.error("SAX issue with "+file.getName(), e);
-            }
-            catch (ParseException e) {
-                logger.error("XML issue with "+file.getName(), e);
-            }
+            sobiFile.incrementProcessedCount();
+            sobiFile.setProcessedDateTime(new Date());
+            sobiFile.setPendingProcessing(false);
+            sobiFileDao.updateSOBIFile(sobiFile);
         }
-        storage.flush();
+
+        // TODO: Process Transcripts / Public Hearings
+        // TODO: Handle CMS.TEXT (Rules file)
     }
 
     /**
@@ -311,8 +201,7 @@ public void ingest(File workingDir, Storage storage) throws IOException, ParserC
      * @param changes - A list of changes to push out to all services
      * @param services - A list of services to push change to.
      */
-    public void push(Storage storage, List<Entry<String, Change>> changes, List<ServiceBase> services)
-    {
+    public void push(Storage storage, List<Entry<String, Change>> changes, List<ServiceBase> services) {
         for(ServiceBase service : services) {
             try {
                 service.process(changes, storage);
@@ -332,8 +221,7 @@ public void push(Storage storage, List<Entry<String, Change>> changes, List<Serv
      * @param archiveDir - The directory to archive all work files to.
      * @throws IOException
      */
-    public void archive(File workingDir, File archiveDir) throws IOException
-    {
+    public void archive(File workingDir, File archiveDir) throws IOException {
         File rulesFile = new File(workingDir, "CMS.TEXT");
         if (rulesFile.exists()) {
             moveFileToDirectory(rulesFile, archiveDir, true);
@@ -357,13 +245,20 @@ public void archive(File workingDir, File archiveDir) throws IOException
         archiveFiles(safeGetFolder(workingDir, "annotations"), archiveDir, "annotations");
     }
 
-    public void moveFileToDirectory(File file, File directory, boolean createDirectory) throws IOException
-    {
-        File newFile = new File(directory, file.getName());
-        if (newFile.exists()) {
-            newFile.delete();
+    /** --- Internal Methods --- */
+
+    /**
+     * Check the given SOBI line to determine if it matches the start of a SOBI Fragment type.
+     * @param line String
+     * @return SOBIFragmentType or null if no match
+     */
+    protected SOBIFragmentType getFragmentTypeFromLine(String line) {
+        for (SOBIFragmentType fragmentType : SOBIFragmentType.values()) {
+            if (line.matches(fragmentType.getStartPattern())) {
+                return fragmentType;
+            }
         }
-        FileUtils.moveFileToDirectory(file, directory, true);
+        return null;
     }
 
     /**
@@ -377,8 +272,7 @@ public void moveFileToDirectory(File file, File directory, boolean createDirecto
      * @param subFolder - The name of the sub-directory to store the files. Cannot be null!
      * @throws IOException
      */
-    protected void archiveFiles(File sourceDir, File destDir, String subFolder) throws IOException
-    {
+    protected void archiveFiles(File sourceDir, File destDir, String subFolder) throws IOException {
         Calendar calendar = Calendar.getInstance();
         SimpleDateFormat sobiDateFormat = new SimpleDateFormat("'SOBI.D'yyMMdd'.T'HHmmss'.TXT'");
         for (File file : safeListFiles(sourceDir, null, false)) {
@@ -394,47 +288,33 @@ protected void archiveFiles(File sourceDir, File destDir, String subFolder) thro
     }
 
     /**
-     * Returns a collection of files sorted by file name (not file path!)
-     *
-     * @param directory - The directory to get files from.
-     * @param recursive - true to retrieve files from sub-directories.
-     * @return
-     * @throws IOException
-     */
-    protected Collection<File> getSortedFiles(File directory, boolean recursive, String[] excludeDirs) throws IOException {
-        Collection<File> files = safeListFiles(directory, recursive, excludeDirs);
-        Collections.sort((List<File>)files, new Comparator<File>(){
-            public int compare(File a, File b) {
-                return a.getName().compareTo(b.getName());
-            }
-        });
-        return files;
-    }
-
-    /**
-     * Extracts a well formed XML document from a BufferedReader and writes it to the given
+     * Extracts a well formed XML document from the lines and writes it to the given
      * file. This depends strongly on escape sequences being on their own line; otherwise
-     * we'll get malformed XML docs.
+     * we'll get malformed XML docs. TODO: EDIT THIS DOC
+     *
+     * @param fragmentType SOBIFragmentType
+     * @param line String - The starting line of the document
+     * @param iterator Iterator<String>
      *
-     * @param escape - Regex matching the escape sequence for the XML document. e.g. {@literal </senagenda.+}
-     * @param line - The starting line of the document
-     * @param br - The buffered reader used to retrieve additional lines from the document
-     * @param file - The file that the resulting XML document should be written to.
+     * @return String - The resulting XML string.
      * @throws IOException
      */
-    protected void extractXml(String escape, String line, BufferedReader br, File file) throws IOException
-    {
+    protected String extractXmlText(SOBIFragmentType fragmentType, String line, Iterator<String> iterator) throws IOException {
+        String endPattern = fragmentType.getEndPattern();
+
         StringBuffer sb = new StringBuffer(
             "<?xml version='1.0' encoding='UTF-8'?>&newl;" +
             "<SENATEDATA>&newl;" +
              line+"&newl;"
         );
 
-        String in;
-        while((in = br.readLine()) != null) {
+        String in = null;
+        while (iterator.hasNext()) {
+            in = iterator.next();
             sb.append(in.replaceAll("\\xb9","&sect;") + "&newl;");
-            if(in.matches(escape))
+            if (in.matches(endPattern)) {
                 break;
+            }
         }
 
         if (in == null) {
@@ -445,7 +325,7 @@ protected void extractXml(String escape, String line, BufferedReader br, File fi
 
         String data = sb.append("</SENATEDATA>").toString();
 
-        // TODO: Figure out all this matcher magic. How does it work? What the hell is it doing?
+        // TODO: Figure out all this matcher magic. How does it work?
         sb = new StringBuffer();
         Matcher m = Pattern.compile("<\\!\\[CDATA\\[(.*?)\\]\\]>").matcher(data);
         while(m.find()) {
@@ -455,6 +335,6 @@ protected void extractXml(String escape, String line, BufferedReader br, File fi
 
         // TODO: What exactly are we replacing here?
         data = sb.toString().replaceAll("&newl;", "\n").replaceAll("(?!\n)\\p{Cntrl}","").replaceAll("(?!\\.{2})[ ]{2,}"," ");
-        FileUtils.write(file, data);
+        return data;
     }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FDataProcessor.java
cda4867f39a44931080a46a83a0a70e0cad703bc
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FPublicHearingProcessor.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FPublicHearingProcessor.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/processors/PublicHearingProcessor.java
import gov.nysenate.openleg.model.transcript.PublicHearing;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import gov.nysenate.openleg.model.PublicHearing;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;

@@ -1,8 +1,10 @@
 package gov.nysenate.openleg.processors;
 
-import gov.nysenate.openleg.model.PublicHearing;
+import gov.nysenate.openleg.model.transcript.PublicHearing;
 import gov.nysenate.openleg.util.Storage;
 import gov.nysenate.openleg.util.TextFormatter;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
 
 import java.io.File;
 import java.io.IOException;
@@ -12,9 +14,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.io.FileUtils;
-import org.apache.log4j.Logger;
-
 public class PublicHearingProcessor {
     private final Logger logger;
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FPublicHearingProcessor.java
e5832a58ff1dcbd093c5d0f8fed3a387eeca6b0c
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FSenagendaProcessor.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FSenagendaProcessor.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/processors/SenagendaProcessor.java
import gov.nysenate.openleg.model.agenda.*;
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.bill.BillAmendment;
import gov.nysenate.openleg.model.entity.Person;
import gov.nysenate.openleg.model.sobi.SOBIBlock;
import gov.nysenate.openleg.processors.sobi.BillProcessor;
import gov.nysenate.openleg.util.*;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.xpath.XPathExpressionException;
    public static Map<String, VoteAction> VOTE_ACTION_MAP = new TreeMap<>();
        BillProcessor processor = new BillProcessor(Application.getEnvironment());
        Bill bill = null; /** FIXME processor.getOrCreateBaseBill(mockBlock, modifiedDate, storage); */
            /** FIXME processor.saveBill(bill, billAmendment, storage); */
            /** FIXME new BillProcessor(Application.getEnvironment()).saveBill(bill, Bill.BASE_VERSION, storage); */
import gov.nysenate.openleg.model.Agenda;
import gov.nysenate.openleg.model.AgendaInfoAddendum;
import gov.nysenate.openleg.model.AgendaInfoCommittee;
import gov.nysenate.openleg.model.AgendaInfoCommitteeItem;
import gov.nysenate.openleg.model.AgendaVoteAddendum;
import gov.nysenate.openleg.model.AgendaVoteCommittee;
import gov.nysenate.openleg.model.AgendaVoteCommitteeAttendance;
import gov.nysenate.openleg.model.AgendaVoteCommitteeItem;
import gov.nysenate.openleg.model.AgendaVoteCommitteeVote;
import gov.nysenate.openleg.model.Bill;
import gov.nysenate.openleg.model.BillAmendment;
import gov.nysenate.openleg.model.Person;
import gov.nysenate.openleg.model.SOBIBlock;
import gov.nysenate.openleg.util.Application;
import gov.nysenate.openleg.util.ChangeLogger;
import gov.nysenate.openleg.util.DateHelper;
import gov.nysenate.openleg.util.Storage;
import gov.nysenate.openleg.util.XmlHelper;
import javax.xml.xpath.XPathExpressionException;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

    public static Map<String, VoteAction> VOTE_ACTION_MAP = new TreeMap<String, VoteAction>();
        BillProcessor processor = new BillProcessor();
        Bill bill = processor.getOrCreateBaseBill(mockBlock, modifiedDate, storage);
            processor.saveBill(bill, billAmendment, storage);
            new BillProcessor().saveBill(bill, Bill.BASE_VERSION, storage);
@@ -1,24 +1,19 @@
 package gov.nysenate.openleg.processors;
 
-import gov.nysenate.openleg.model.Agenda;
-import gov.nysenate.openleg.model.AgendaInfoAddendum;
-import gov.nysenate.openleg.model.AgendaInfoCommittee;
-import gov.nysenate.openleg.model.AgendaInfoCommitteeItem;
-import gov.nysenate.openleg.model.AgendaVoteAddendum;
-import gov.nysenate.openleg.model.AgendaVoteCommittee;
-import gov.nysenate.openleg.model.AgendaVoteCommitteeAttendance;
-import gov.nysenate.openleg.model.AgendaVoteCommitteeItem;
-import gov.nysenate.openleg.model.AgendaVoteCommitteeVote;
-import gov.nysenate.openleg.model.Bill;
-import gov.nysenate.openleg.model.BillAmendment;
-import gov.nysenate.openleg.model.Person;
-import gov.nysenate.openleg.model.SOBIBlock;
-import gov.nysenate.openleg.util.Application;
-import gov.nysenate.openleg.util.ChangeLogger;
-import gov.nysenate.openleg.util.DateHelper;
-import gov.nysenate.openleg.util.Storage;
-import gov.nysenate.openleg.util.XmlHelper;
+import gov.nysenate.openleg.model.agenda.*;
+import gov.nysenate.openleg.model.bill.Bill;
+import gov.nysenate.openleg.model.bill.BillAmendment;
+import gov.nysenate.openleg.model.entity.Person;
+import gov.nysenate.openleg.model.sobi.SOBIBlock;
+import gov.nysenate.openleg.processors.sobi.BillProcessor;
+import gov.nysenate.openleg.util.*;
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
 
+import javax.xml.xpath.XPathExpressionException;
 import java.io.File;
 import java.io.IOException;
 import java.text.ParseException;
@@ -27,20 +22,12 @@
 import java.util.Map;
 import java.util.TreeMap;
 
-import javax.xml.xpath.XPathExpressionException;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
 public class SenagendaProcessor
 {
     private final Logger logger = Logger.getLogger(SenagendaProcessor.class);
 
     public static enum VoteAction { FIRST_READING, THIRD_READING, REFERRED_TO_COMMITTEE, DEFEATED, RESTORED_TO_THIRD, SPECIAL}
-    public static Map<String, VoteAction> VOTE_ACTION_MAP = new TreeMap<String, VoteAction>();
+    public static Map<String, VoteAction> VOTE_ACTION_MAP = new TreeMap<>();
     static {
         VOTE_ACTION_MAP.put("F", VoteAction.FIRST_READING);
         VOTE_ACTION_MAP.put("3", VoteAction.THIRD_READING);
@@ -237,16 +224,16 @@ private Bill getOrCreateBill(Storage storage, String printNo, String billAmendme
 
         // All bills on calendars should already exist but sometimes, particularly during development/testing
         // they won't. Instead of breaking the processing, create a new bill using the bill processor.
-        BillProcessor processor = new BillProcessor();
+        BillProcessor processor = new BillProcessor(Application.getEnvironment());
         SOBIBlock mockBlock = new SOBIBlock(year+printNo+(printNo.matches("[A-Z]$") ? "" : " ")+1+"     ");
-        Bill bill = processor.getOrCreateBaseBill(mockBlock, modifiedDate, storage);
+        Bill bill = null; /** FIXME processor.getOrCreateBaseBill(mockBlock, modifiedDate, storage); */
         bill.setSponsor(new Person(sponsors[0].trim()));
 
         // It must be published if it is on the agenda
         BillAmendment amendment = bill.getAmendment(billAmendment);
         if (!amendment.isPublished()) {
             amendment.setPublishDate(modifiedDate);
-            processor.saveBill(bill, billAmendment, storage);
+            /** FIXME processor.saveBill(bill, billAmendment, storage); */
         }
 
         // Other sponsors are removed when a calendar/agenda is resent without
@@ -258,7 +245,7 @@ private Bill getOrCreateBill(Storage storage, String printNo, String billAmendme
 
         if (!bill.getOtherSponsors().equals(otherSponsors)) {
             bill.setOtherSponsors(otherSponsors);
-            new BillProcessor().saveBill(bill, Bill.BASE_VERSION, storage);
+            /** FIXME new BillProcessor(Application.getEnvironment()).saveBill(bill, Bill.BASE_VERSION, storage); */
         }
 
         return bill;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FSenagendaProcessor.java
3da895d30e6a48f7a6e482acd5bca8d8e0204ce7
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FSencalendarProcessor.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FSencalendarProcessor.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/processors/SencalendarProcessor.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.bill.BillAmendment;
import gov.nysenate.openleg.model.calendar.*;
import gov.nysenate.openleg.model.entity.Person;
import gov.nysenate.openleg.model.sobi.SOBIBlock;
import gov.nysenate.openleg.processors.sobi.BillProcessor;
import gov.nysenate.openleg.util.*;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.xpath.XPathExpressionException;
        BillProcessor processor = new BillProcessor(Application.getEnvironment());
        Bill bill = null; /**FIXME processor.getOrCreateBaseBill(mockBlock, modifiedDate, storage); */
                /** FIXME processor.saveBill(bill, billAmendment, storage); */
            /** FIXME processor.saveBill(bill, billAmendment, storage); */
import gov.nysenate.openleg.model.Bill;
import gov.nysenate.openleg.model.BillAmendment;
import gov.nysenate.openleg.model.Calendar;
import gov.nysenate.openleg.model.CalendarActiveList;
import gov.nysenate.openleg.model.CalendarActiveListEntry;
import gov.nysenate.openleg.model.CalendarSupplemental;
import gov.nysenate.openleg.model.CalendarSupplementalSection;
import gov.nysenate.openleg.model.CalendarSupplementalSectionEntry;
import gov.nysenate.openleg.model.Person;
import gov.nysenate.openleg.model.SOBIBlock;
import gov.nysenate.openleg.util.Application;
import gov.nysenate.openleg.util.ChangeLogger;
import gov.nysenate.openleg.util.DateHelper;
import gov.nysenate.openleg.util.Storage;
import gov.nysenate.openleg.util.XmlHelper;
import javax.xml.xpath.XPathExpressionException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

        BillProcessor processor = new BillProcessor();
        Bill bill = processor.getOrCreateBaseBill(mockBlock, modifiedDate, storage);
                processor.saveBill(bill, billAmendment, storage);
            processor.saveBill(bill, billAmendment, storage);
@@ -1,33 +1,23 @@
 package gov.nysenate.openleg.processors;
 
-import gov.nysenate.openleg.model.Bill;
-import gov.nysenate.openleg.model.BillAmendment;
-import gov.nysenate.openleg.model.Calendar;
-import gov.nysenate.openleg.model.CalendarActiveList;
-import gov.nysenate.openleg.model.CalendarActiveListEntry;
-import gov.nysenate.openleg.model.CalendarSupplemental;
-import gov.nysenate.openleg.model.CalendarSupplementalSection;
-import gov.nysenate.openleg.model.CalendarSupplementalSectionEntry;
-import gov.nysenate.openleg.model.Person;
-import gov.nysenate.openleg.model.SOBIBlock;
-import gov.nysenate.openleg.util.Application;
-import gov.nysenate.openleg.util.ChangeLogger;
-import gov.nysenate.openleg.util.DateHelper;
-import gov.nysenate.openleg.util.Storage;
-import gov.nysenate.openleg.util.XmlHelper;
+import gov.nysenate.openleg.model.bill.Bill;
+import gov.nysenate.openleg.model.bill.BillAmendment;
+import gov.nysenate.openleg.model.calendar.*;
+import gov.nysenate.openleg.model.entity.Person;
+import gov.nysenate.openleg.model.sobi.SOBIBlock;
+import gov.nysenate.openleg.processors.sobi.BillProcessor;
+import gov.nysenate.openleg.util.*;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
 
+import javax.xml.xpath.XPathExpressionException;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Date;
 
-import javax.xml.xpath.XPathExpressionException;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
 public class SencalendarProcessor
 {
     public Calendar getOrCreateCalendar(Integer calno, Integer sessYr, Integer year, Storage storage, Date date)
@@ -166,9 +156,9 @@ private Bill getOrCreateBill(Storage storage, String billId, String billAmendmen
         // This is a crappy situation, all bills on calendars should already exist but sometimes they won't.
         // This almost exclusively because we are missing sobi files. It shouldn't happen in production but
         // does frequently in development.
-        BillProcessor processor = new BillProcessor();
+        BillProcessor processor = new BillProcessor(Application.getEnvironment());
         SOBIBlock mockBlock = new SOBIBlock(year+billId+(billId.matches("[A-Z]$") ? "" : " ")+1+"     ");
-        Bill bill = processor.getOrCreateBaseBill(mockBlock, modifiedDate, storage);
+        Bill bill = null; /**FIXME processor.getOrCreateBaseBill(mockBlock, modifiedDate, storage); */
 
         // Active lists don't have sponsor listed, so check first!
         if (sponsorName != null) {
@@ -184,15 +174,15 @@ private Bill getOrCreateBill(Storage storage, String billId, String billAmendmen
 
             if (!bill.getOtherSponsors().equals(otherSponsors)) {
                 bill.setOtherSponsors(otherSponsors);
-                processor.saveBill(bill, billAmendment, storage);
+                /** FIXME processor.saveBill(bill, billAmendment, storage); */
             }
         }
 
         // It must be published if it is on the calendar
         BillAmendment amendment = bill.getAmendment(billAmendment);
         if (!amendment.isPublished()) {
             amendment.setPublishDate(modifiedDate);
-            processor.saveBill(bill, billAmendment, storage);
+            /** FIXME processor.saveBill(bill, billAmendment, storage); */
         }
 
         return bill;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fprocessors%2FSencalendarProcessor.java
496948e613cb11129d44c83d4fbe654c85aa9abc
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FBaseScript.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FBaseScript.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/BaseScript.java
import org.apache.commons.cli.*;
import org.apache.log4j.Logger;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.MissingOptionException;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.log4j.Logger;

@@ -1,18 +1,11 @@
 package gov.nysenate.openleg.scripts;
 
 import gov.nysenate.openleg.util.Application;
+import org.apache.commons.cli.*;
+import org.apache.log4j.Logger;
 
 import java.io.File;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.MissingOptionException;
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.PosixParser;
-import org.apache.log4j.Logger;
-
 /**
  * Provides a base wrapping layer for scripts that logs all uncaught exceptions
  * and provides utility functions for processing options and help messages.
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FBaseScript.java
ddf3db2a7729e7c04fb31f11fd145015e78539dd
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FLRSScraper.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FLRSScraper.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/LRSScraper.java
import org.apache.commons.cli.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

@@ -1,5 +1,10 @@
 package gov.nysenate.openleg.scripts;
 
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
 import java.io.File;
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -9,11 +14,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.log4j.Logger;
-
 public class LRSScraper extends BaseScript
 {
     private final Logger logger = Logger.getLogger(LRSScraper.class);
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FLRSScraper.java
7c6411b3ed69de845b58ef5a0ef61c8113a39699
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FMemoCheck.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FMemoCheck.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/MemoCheck.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.sobi.SOBIBlock;
import gov.nysenate.openleg.processors.sobi.BillProcessor;
import gov.nysenate.openleg.util.Application;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
        BillProcessor bp = new BillProcessor(Application.getEnvironment());
        List<SOBIBlock> blocks = null; //FIXME: bp.getBlocks(blockFile);
            String billNo = block.getBasePrintNo()+block.getAmendment()+"-"+block.getYear();
            Bill jsonBill = storage.getBill(block.getBasePrintNo()+block.getAmendment(), block.getYear());
//            bp.applyText(block.getData(), lbdcBill, "", new Date());
//
//            String jsonMemo = StringUtils.normalizeSpace(jsonBill.getMemo().replaceAll("[?�]","§").replaceAll("-\n+ *", "").replaceAll("\n *", " ").replaceAll(" *([:,]) *", "$1").replaceAll(" *([()!\\\"]) *", " $1 ").replaceAll("([A-Za-z])- ?([A-Za-z])","$1$2").trim()).toLowerCase();
//            String lbdcMemo = StringUtils.normalizeSpace(lbdcBill.getMemo().replaceAll("[?�]","§").replaceAll("-\n+ *", "").replaceAll("\n *", " ").replaceAll(" *([:,]) *", "$1").replaceAll(" *([()!\\\"]) *", " $1 ").replaceAll("([A-Za-z])- ?([A-Za-z])","$1$2").trim()).toLowerCase();
//
//            if(jsonMemo.isEmpty()) {
//                logger.error(billNo+": MISSING");
//                errors.put("missing", errors.get("missing")+1);
//            }
//            else if (!jsonMemo.equals(lbdcMemo)) {
//                logger.error(billNo+": MISMATCH");
//                errors.put("mismatch", errors.get("mismatch")+1);
//                System.out.println(get_diff(jsonMemo, lbdcMemo));
//        System.out.println(errors);
//        System.out.println("Total bills "+blocks.size());
//    }
import gov.nysenate.openleg.model.Bill;
import gov.nysenate.openleg.model.SOBIBlock;
import gov.nysenate.openleg.processors.BillProcessor;
import java.util.Date;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

        BillProcessor bp = new BillProcessor();
        List<SOBIBlock> blocks = bp.getBlocks(blockFile);
            String billNo = block.getPrintNo()+block.getAmendment()+"-"+block.getYear();
            Bill jsonBill = storage.getBill(block.getPrintNo()+block.getAmendment(), block.getYear());
            bp.applyText(block.getData(), lbdcBill, "", new Date());

            String jsonMemo = StringUtils.normalizeSpace(jsonBill.getMemo().replaceAll("[?�]","§").replaceAll("-\n+ *", "").replaceAll("\n *", " ").replaceAll(" *([:,]) *", "$1").replaceAll(" *([()!\\\"]) *", " $1 ").replaceAll("([A-Za-z])- ?([A-Za-z])","$1$2").trim()).toLowerCase();
            String lbdcMemo = StringUtils.normalizeSpace(lbdcBill.getMemo().replaceAll("[?�]","§").replaceAll("-\n+ *", "").replaceAll("\n *", " ").replaceAll(" *([:,]) *", "$1").replaceAll(" *([()!\\\"]) *", " $1 ").replaceAll("([A-Za-z])- ?([A-Za-z])","$1$2").trim()).toLowerCase();

            if(jsonMemo.isEmpty()) {
                logger.error(billNo+": MISSING");
                errors.put("missing", errors.get("missing")+1);
            }
            else if (!jsonMemo.equals(lbdcMemo)) {
                logger.error(billNo+": MISMATCH");
                errors.put("mismatch", errors.get("mismatch")+1);
                System.out.println(get_diff(jsonMemo, lbdcMemo));
        System.out.println(errors);
        System.out.println("Total bills "+blocks.size());
    }
@@ -1,22 +1,20 @@
 package gov.nysenate.openleg.scripts;
 
-import gov.nysenate.openleg.model.Bill;
-import gov.nysenate.openleg.model.SOBIBlock;
-import gov.nysenate.openleg.processors.BillProcessor;
+import gov.nysenate.openleg.model.bill.Bill;
+import gov.nysenate.openleg.model.sobi.SOBIBlock;
+import gov.nysenate.openleg.processors.sobi.BillProcessor;
+import gov.nysenate.openleg.util.Application;
 import gov.nysenate.openleg.util.Storage;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.log4j.Logger;
-
 public class MemoCheck extends BaseScript
 {
     private final Logger logger = Logger.getLogger(MemoCheck.class);
@@ -37,33 +35,33 @@ protected void execute(CommandLine opts) throws Exception
         errors.put("missing",0);
         errors.put("mismatch", 0);
         File blockFile = new File("/data/openleg/PALMER.SEN.ALL.MEMO2011.TXT");
-        BillProcessor bp = new BillProcessor();
+        BillProcessor bp = new BillProcessor(Application.getEnvironment());
         File storageDir = new File("/data/openleg/2011_test/json/");
         Storage storage = new Storage(storageDir);
-        List<SOBIBlock> blocks = bp.getBlocks(blockFile);
+        List<SOBIBlock> blocks = null; //FIXME: bp.getBlocks(blockFile);
         for (SOBIBlock block : blocks) {
-            String billNo = block.getPrintNo()+block.getAmendment()+"-"+block.getYear();
-            Bill jsonBill = storage.getBill(block.getPrintNo()+block.getAmendment(), block.getYear());
+            String billNo = block.getBasePrintNo()+block.getAmendment()+"-"+block.getYear();
+            Bill jsonBill = storage.getBill(block.getBasePrintNo()+block.getAmendment(), block.getYear());
             Bill lbdcBill = new Bill(billNo, block.getYear());
-            bp.applyText(block.getData(), lbdcBill, "", new Date());
-
-            String jsonMemo = StringUtils.normalizeSpace(jsonBill.getMemo().replaceAll("[?�]","§").replaceAll("-\n+ *", "").replaceAll("\n *", " ").replaceAll(" *([:,]) *", "$1").replaceAll(" *([()!\\\"]) *", " $1 ").replaceAll("([A-Za-z])- ?([A-Za-z])","$1$2").trim()).toLowerCase();
-            String lbdcMemo = StringUtils.normalizeSpace(lbdcBill.getMemo().replaceAll("[?�]","§").replaceAll("-\n+ *", "").replaceAll("\n *", " ").replaceAll(" *([:,]) *", "$1").replaceAll(" *([()!\\\"]) *", " $1 ").replaceAll("([A-Za-z])- ?([A-Za-z])","$1$2").trim()).toLowerCase();
-
-            if(jsonMemo.isEmpty()) {
-                logger.error(billNo+": MISSING");
-                errors.put("missing", errors.get("missing")+1);
-            }
-            else if (!jsonMemo.equals(lbdcMemo)) {
-                logger.error(billNo+": MISMATCH");
-                errors.put("mismatch", errors.get("mismatch")+1);
-                System.out.println(get_diff(jsonMemo, lbdcMemo));
+//            bp.applyText(block.getData(), lbdcBill, "", new Date());
+//
+//            String jsonMemo = StringUtils.normalizeSpace(jsonBill.getMemo().replaceAll("[?�]","§").replaceAll("-\n+ *", "").replaceAll("\n *", " ").replaceAll(" *([:,]) *", "$1").replaceAll(" *([()!\\\"]) *", " $1 ").replaceAll("([A-Za-z])- ?([A-Za-z])","$1$2").trim()).toLowerCase();
+//            String lbdcMemo = StringUtils.normalizeSpace(lbdcBill.getMemo().replaceAll("[?�]","§").replaceAll("-\n+ *", "").replaceAll("\n *", " ").replaceAll(" *([:,]) *", "$1").replaceAll(" *([()!\\\"]) *", " $1 ").replaceAll("([A-Za-z])- ?([A-Za-z])","$1$2").trim()).toLowerCase();
+//
+//            if(jsonMemo.isEmpty()) {
+//                logger.error(billNo+": MISSING");
+//                errors.put("missing", errors.get("missing")+1);
+//            }
+//            else if (!jsonMemo.equals(lbdcMemo)) {
+//                logger.error(billNo+": MISMATCH");
+//                errors.put("mismatch", errors.get("mismatch")+1);
+//                System.out.println(get_diff(jsonMemo, lbdcMemo));
 //                System.in.read();
             }
         }
-        System.out.println(errors);
-        System.out.println("Total bills "+blocks.size());
-    }
+//        System.out.println(errors);
+//        System.out.println("Total bills "+blocks.size());
+//    }
 
     public static void main(String[] args) throws Exception
     {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FMemoCheck.java
738a674f5d0964d4c80b91b493ecd8158c0e32c3
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FProcessData.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FProcessData.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/ProcessData.java
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
            if (target.equals("reporter")) {
        DataProcessor process = new DataProcessor(env);
            switch (task) {
                case "stage": process.stage(env.getStagingDirectory(), env.getWorkingDirectory()); break;
                case "collate": process.collate(env.getWorkingDirectory()); break;
                case "ingest": process.ingest(env.getWorkingDirectory(), Application.getStorage()); break;
                case "archive": process.archive(env.getWorkingDirectory(), env.getArchiveDirectory()); break;
                default:
                    System.err.println("Invalid task.");
                    System.exit(1);
import gov.nysenate.openleg.services.Lucene;
import gov.nysenate.openleg.services.Varnish;
import gov.nysenate.openleg.util.ChangeLogger;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;

            if (target.equals("lucene")) {
                services.add(new Lucene());
            }
            else if (target.equals("varnish")) {
                services.add(new Varnish("127.0.0.1", 80));
            }
            else if (target.equals("reporter")) {
        DataProcessor process = new DataProcessor();
            // TODO: With Java7 we can make this a switch case
            if (task.equals("stage")) {
                process.stage(env.getStagingDirectory(), env.getWorkingDirectory());
            }
            else if (task.equals("collate")) {
                process.collate(env.getWorkingDirectory());
            }
            else if (task.equals("ingest")) {
                process.ingest(env.getWorkingDirectory(), Application.getStorage());
                if (changeFile != null) {
                    ChangeLogger.writeToFile(changeFile);
                }
            }
            else if (task.equals("push")) {
                if (ChangeLogger.getChangeLog().isEmpty()) {
                    if (changeFile != null) {
                        ChangeLogger.readFromFile(changeFile);
                    }
                    else {
                        System.err.println("Unable to push with an empty change log.");
                    }
                }
                process.push(Application.getStorage(), ChangeLogger.getEntries(), services);
            }
            else if (task.equalsIgnoreCase("archive")) {
                process.archive(env.getWorkingDirectory(), env.getArchiveDirectory());
            }
            else {
                System.err.println("Invalid task.");
                System.exit(1);
@@ -2,21 +2,17 @@
 
 import gov.nysenate.openleg.Environment;
 import gov.nysenate.openleg.processors.DataProcessor;
-import gov.nysenate.openleg.services.Lucene;
 import gov.nysenate.openleg.services.ServiceBase;
 import gov.nysenate.openleg.services.UpdateReporter;
-import gov.nysenate.openleg.services.Varnish;
 import gov.nysenate.openleg.util.Application;
-import gov.nysenate.openleg.util.ChangeLogger;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Options;
 
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.Options;
-
 /**
  * Usage: bin/run.sh ProcessData --environment app.properties --tasks collate,ingest,push,archive --push-targets lucene,varnish,reporter --change-file logs/D20130511.T123500.change.log &>> logs/D20130511.T123500.process.log
  *
@@ -73,13 +69,7 @@ else if (tasks.contains("push")) {
 
         ArrayList<ServiceBase> services = new ArrayList<ServiceBase>();
         for (String target : pushTargets) {
-            if (target.equals("lucene")) {
-                services.add(new Lucene());
-            }
-            else if (target.equals("varnish")) {
-                services.add(new Varnish("127.0.0.1", 80));
-            }
-            else if (target.equals("reporter")) {
+            if (target.equals("reporter")) {
                 services.add(new UpdateReporter());
             }
             else {
@@ -90,38 +80,16 @@ else if (target.equals("reporter")) {
         }
 
         Environment env = Application.getEnvironment();
-        DataProcessor process = new DataProcessor();
+        DataProcessor process = new DataProcessor(env);
         for (String task : tasks) {
-            // TODO: With Java7 we can make this a switch case
-            if (task.equals("stage")) {
-                process.stage(env.getStagingDirectory(), env.getWorkingDirectory());
-            }
-            else if (task.equals("collate")) {
-                process.collate(env.getWorkingDirectory());
-            }
-            else if (task.equals("ingest")) {
-                process.ingest(env.getWorkingDirectory(), Application.getStorage());
-                if (changeFile != null) {
-                    ChangeLogger.writeToFile(changeFile);
-                }
-            }
-            else if (task.equals("push")) {
-                if (ChangeLogger.getChangeLog().isEmpty()) {
-                    if (changeFile != null) {
-                        ChangeLogger.readFromFile(changeFile);
-                    }
-                    else {
-                        System.err.println("Unable to push with an empty change log.");
-                    }
-                }
-                process.push(Application.getStorage(), ChangeLogger.getEntries(), services);
-            }
-            else if (task.equalsIgnoreCase("archive")) {
-                process.archive(env.getWorkingDirectory(), env.getArchiveDirectory());
-            }
-            else {
-                System.err.println("Invalid task.");
-                System.exit(1);
+            switch (task) {
+                case "stage": process.stage(env.getStagingDirectory(), env.getWorkingDirectory()); break;
+                case "collate": process.collate(env.getWorkingDirectory()); break;
+                case "ingest": process.ingest(env.getWorkingDirectory(), Application.getStorage()); break;
+                case "archive": process.archive(env.getWorkingDirectory(), env.getArchiveDirectory()); break;
+                default:
+                    System.err.println("Invalid task.");
+                    System.exit(1);
             }
         }
     }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FProcessData.java
7c78ce39442fa46a7e0078016b51aec3cdf5da81
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FTextUpdateFrequency.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FTextUpdateFrequency.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/TextUpdateFrequency.java
import org.apache.commons.io.FileUtils;

import org.apache.commons.io.FileUtils;

@@ -1,5 +1,7 @@
 package gov.nysenate.openleg.scripts;
 
+import org.apache.commons.io.FileUtils;
+
 import java.io.File;
 import java.io.IOException;
 import java.text.ParseException;
@@ -9,8 +11,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.io.FileUtils;
-
 public class TextUpdateFrequency
 {
     public static void main(String[] args) throws IOException, ParseException
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FTextUpdateFrequency.java
b524d5143a7a695b116c1bc9899585b93aeac7fa
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FUpdateNYSenateData.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FUpdateNYSenateData.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/UpdateNYSenateData.java
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@@ -7,13 +7,6 @@
 import gov.nysenate.services.model.Committee;
 import gov.nysenate.services.model.Member;
 import gov.nysenate.services.model.Senator;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Options;
 import org.apache.commons.collections.ListUtils;
@@ -23,6 +16,12 @@
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.SerializationConfig.Feature;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 public class UpdateNYSenateData extends BaseScript
 {
     public static Logger logger = Logger.getLogger(UpdateNYSenateData.class);
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2FUpdateNYSenateData.java
1ce67d70a3797418fcb77ad56193a5812e5991a4
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FCheckMail.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FCheckMail.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/admin/CheckMail.java
import org.apache.commons.cli.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import javax.mail.*;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;


@@ -1,25 +1,17 @@
 package gov.nysenate.openleg.scripts.admin;
 
 import gov.nysenate.openleg.scripts.BaseScript;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
 
+import javax.mail.*;
 import java.io.File;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Properties;
 
-import javax.mail.Flags;
-import javax.mail.Folder;
-import javax.mail.Message;
-import javax.mail.Multipart;
-import javax.mail.Part;
-import javax.mail.Session;
-import javax.mail.Store;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.log4j.Logger;
-
 public class CheckMail extends BaseScript
 {
     private static final Logger logger = Logger.getLogger(CheckMail.class);
@@ -100,5 +92,4 @@ else if (message.getSubject().contains("Job ABPSDD - LBDC all Bills")) {
             new CreateErrors().execute(opts);
         }
     }
-
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FCheckMail.java
c58ddd4aa203b49751b75a08ec11b2ec0e37e5ca
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FCreateErrors.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FCreateErrors.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/admin/CreateErrors.java
import java.sql.Connection;
import java.util.Collection;


import java.sql.Connection;
import java.util.Collection;

@@ -5,15 +5,14 @@
 import gov.nysenate.openleg.model.admin.ReportObservation;
 import gov.nysenate.openleg.scripts.BaseScript;
 import gov.nysenate.openleg.util.Application;
-
-import java.sql.Connection;
-import java.util.Collection;
-
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.dbutils.QueryRunner;
 import org.apache.commons.dbutils.handlers.BeanListHandler;
 import org.apache.log4j.Logger;
 
+import java.sql.Connection;
+import java.util.Collection;
+
 public class CreateErrors extends BaseScript
 {
     public static Logger logger = Logger.getLogger(CreateErrors.class);
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FCreateErrors.java
b8f18c1a6b89f20248f14690fbe5170a15f8298a
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FSpotCheck.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FSpotCheck.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/scripts/admin/SpotCheck.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.bill.BillAction;
import gov.nysenate.openleg.model.entity.Person;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.log4j.Logger;
import java.util.*;
            String jsonLaw = bill.getLaw();
            if ( bill.getAmendment(billAmendment).getCoSponsors() != null ) {
                List<Person> cosponsors = bill.getAmendment(billAmendment).getCoSponsors();
            for (String line : bill.getAmendment(billAmendment).getFulltext().split("\n")) {
import gov.nysenate.openleg.model.Bill;
import gov.nysenate.openleg.model.BillAction;
import gov.nysenate.openleg.model.Person;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.log4j.Logger;

            String jsonLaw = bill.getLaw(billAmendment);
            if ( bill.getCoSponsors() != null ) {
                List<Person> cosponsors = bill.getCoSponsors(billAmendment);
            for (String line : bill.getFulltext(billAmendment).split("\n")) {
@@ -1,36 +1,31 @@
 package gov.nysenate.openleg.scripts.admin;
 
-import gov.nysenate.openleg.model.Bill;
-import gov.nysenate.openleg.model.BillAction;
-import gov.nysenate.openleg.model.Person;
 import gov.nysenate.openleg.model.admin.Report;
 import gov.nysenate.openleg.model.admin.ReportObservation;
 import gov.nysenate.openleg.model.admin.SpotCheckBill;
+import gov.nysenate.openleg.model.bill.Bill;
+import gov.nysenate.openleg.model.bill.BillAction;
+import gov.nysenate.openleg.model.entity.Person;
 import gov.nysenate.openleg.scripts.BaseScript;
 import gov.nysenate.openleg.util.Application;
 import gov.nysenate.openleg.util.Storage;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.dbutils.QueryRunner;
+import org.apache.commons.dbutils.handlers.BeanHandler;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.log4j.Logger;
 
 import java.io.File;
 import java.io.IOException;
 import java.sql.SQLException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.TreeSet;
+import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.dbutils.QueryRunner;
-import org.apache.commons.dbutils.handlers.BeanHandler;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.commons.lang3.StringEscapeUtils;
-import org.apache.log4j.Logger;
-
 public class SpotCheck extends BaseScript
 {
     public static Logger logger = Logger.getLogger(SpotCheck.class);
@@ -124,7 +119,7 @@ public void execute(CommandLine opts) throws IOException, ParseException, SQLExc
             }
 
             // Compare the summaries. LBDC reports summary and law changes together
-            String jsonLaw = bill.getLaw(billAmendment);
+            String jsonLaw = bill.getLaw();
             String jsonSummary = unescapeHTML(bill.getSummary());
             String lbdcSummary = spotCheckBills.get(printNo).getSummary().replaceAll("\\s+", " ");
 
@@ -165,8 +160,8 @@ public void execute(CommandLine opts) throws IOException, ParseException, SQLExc
 
             TreeSet<String> lbdcCosponsors = new TreeSet<String>(spotCheckBills.get(printNo).getCosponsors());
             TreeSet<String> jsonCosponsors = new TreeSet<String>();
-            if ( bill.getCoSponsors() != null ) {
-                List<Person> cosponsors = bill.getCoSponsors(billAmendment);
+            if ( bill.getAmendment(billAmendment).getCoSponsors() != null ) {
+                List<Person> cosponsors = bill.getAmendment(billAmendment).getCoSponsors();
                 for(Person cosponsor : cosponsors) {
                     jsonCosponsors.add(cosponsor.getFullname().toUpperCase());
                 }
@@ -205,7 +200,7 @@ public void execute(CommandLine opts) throws IOException, ParseException, SQLExc
             int lbdcPages = spotCheckBills.get(printNo).pages;
             int jsonPages = 0;
             Pattern pagePattern = Pattern.compile("(^\\s+\\w\\.\\s\\d+(--\\w)?\\s+\\d*(\\s+\\w\\.\\s\\d+(--\\w)?)?$|^\\s+\\d+\\s+\\d+\\-\\d+\\-\\d$|^\\s{11,}\\d{1,4}(--\\w)?$)");
-            for (String line : bill.getFulltext(billAmendment).split("\n")) {
+            for (String line : bill.getAmendment(billAmendment).getFulltext().split("\n")) {
                 if (pagePattern.matcher(line).find()) {
                     // logger.info(billNo+": "+line);
                     jsonPages++;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fscripts%2Fadmin%2FSpotCheck.java
82282b14bfca91a1e0aae4b27a33869e3bc6c762
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fservices%2FServiceBase.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fservices%2FServiceBase.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/services/ServiceBase.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.calendar.Calendar;
import gov.nysenate.openleg.model.transcript.Transcript;
import org.apache.log4j.Logger;
import gov.nysenate.openleg.model.Bill;
import gov.nysenate.openleg.model.Calendar;
import gov.nysenate.openleg.model.Transcript;
import org.apache.log4j.Logger;

@@ -1,19 +1,18 @@
 package gov.nysenate.openleg.services;
 
 import gov.nysenate.openleg.model.BaseObject;
-import gov.nysenate.openleg.model.Bill;
-import gov.nysenate.openleg.model.Calendar;
 import gov.nysenate.openleg.model.Change;
-import gov.nysenate.openleg.model.Transcript;
+import gov.nysenate.openleg.model.bill.Bill;
+import gov.nysenate.openleg.model.calendar.Calendar;
+import gov.nysenate.openleg.model.transcript.Transcript;
 import gov.nysenate.openleg.util.Storage;
+import org.apache.log4j.Logger;
 
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map.Entry;
 
-import org.apache.log4j.Logger;
-
 public abstract class ServiceBase {
     protected HashMap<String, Class<? extends BaseObject>> classMap;
     protected Logger logger;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fservices%2FServiceBase.java
fff530d1424f80d357c751aad0832193705b8a0a
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fservices%2FUpdateReporter.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fservices%2FUpdateReporter.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/services/UpdateReporter.java
import org.apache.commons.dbutils.QueryRunner;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.tomcat.jdbc.pool.DataSource;

@@ -3,14 +3,13 @@
 import gov.nysenate.openleg.model.Change;
 import gov.nysenate.openleg.util.Application;
 import gov.nysenate.openleg.util.Storage;
+import org.apache.commons.dbutils.QueryRunner;
+import org.apache.tomcat.jdbc.pool.DataSource;
 
 import java.sql.SQLException;
 import java.util.List;
 import java.util.Map.Entry;
 
-import org.apache.commons.dbutils.QueryRunner;
-import org.apache.tomcat.jdbc.pool.DataSource;
-
 /*
  * Parses changes from a changeLog file and saves to MySQL database.
  */
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fservices%2FUpdateReporter.java
807499ba6314ffacbe2bf59e4a70ac0b0b62124c
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FApplication.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FApplication.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/Application.java
import java.io.IOException;

            appInstance.environment = new Environment(appInstance.config, "env", "master");
            //appInstance.lucene = new Lucene(appInstance.config, "lucene");
       // if (appInstance.lucene != null) {
            //appInstance.lucene.close();
       // }
import gov.nysenate.openleg.lucene.Lucene;

import java.io.IOException;

    protected Lucene lucene;
            appInstance.environment = new Environment(appInstance.config, "env");
            appInstance.lucene = new Lucene(appInstance.config, "lucene");
        if (appInstance.lucene != null) {
            appInstance.lucene.close();
        }
    public static Lucene getLucene() {
        return appInstance.lucene;
    }

src/main/java/gov/nysenate/openleg/util/Application.java
return appInstance.lucene;
Lucene getLucene()
@@ -1,18 +1,16 @@
 package gov.nysenate.openleg.util;
 
 import gov.nysenate.openleg.Environment;
-import gov.nysenate.openleg.lucene.Lucene;
 import gov.nysenate.util.Config;
 import gov.nysenate.util.DB;
 import gov.nysenate.util.Mailer;
 import gov.nysenate.util.listener.NYSenateConfigurationListener;
-
-import java.io.IOException;
-
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.log4j.Logger;
 import org.codehaus.jackson.map.ObjectMapper;
 
+import java.io.IOException;
+
 public class Application
 {
     public static Logger logger = Logger.getLogger(Application.class);
@@ -29,7 +27,6 @@ public class Application
     protected Config config;
     protected Mailer mailer;
     protected Environment environment;
-    protected Lucene lucene;
     protected Storage storage;
     protected XmlHelper xmlhelper;
     protected ObjectMapper objectMapper;
@@ -52,8 +49,8 @@ public static boolean bootstrap(String propertyFileName)
             appInstance.xmlhelper = new XmlHelper(appInstance.config, "xml");
             appInstance.db = new DB(appInstance.config, "postgresdb");
             appInstance.mailer = new Mailer(appInstance.config, "mailer");
-            appInstance.environment = new Environment(appInstance.config, "env");
-            appInstance.lucene = new Lucene(appInstance.config, "lucene");
+            appInstance.environment = new Environment(appInstance.config, "env", "master");
+            //appInstance.lucene = new Lucene(appInstance.config, "lucene");
             appInstance.storage = new Storage(appInstance.environment.getStorageDirectory());
             appInstance.objectMapper = new ObjectMapper();
             return true;
@@ -73,9 +70,9 @@ public static boolean bootstrap(String propertyFileName)
 
     public static boolean shutdown() throws IOException
     {
-        if (appInstance.lucene != null) {
-            appInstance.lucene.close();
-        }
+       // if (appInstance.lucene != null) {
+            //appInstance.lucene.close();
+       // }
         return true;
     }
 
@@ -87,10 +84,6 @@ public static DB getDB() {
         return appInstance.db;
     }
 
-    public static Lucene getLucene() {
-        return appInstance.lucene;
-    }
-
     public static Environment getEnvironment() {
         return appInstance.environment;
     }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FApplication.java
4072ab6966f4a5e3a78b8c30874cd66864d79e2a
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FChangeLogger.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FChangeLogger.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/ChangeLogger.java
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

@@ -2,25 +2,19 @@
 
 import gov.nysenate.openleg.model.Change;
 import gov.nysenate.openleg.util.Storage.Status;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
 
 import java.io.File;
 import java.io.IOException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
 import java.util.Map.Entry;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
-
 public class ChangeLogger
 {
     private static final Logger logger = Logger.getLogger(ChangeLogger.class);
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FChangeLogger.java
c84b463cdd898d4dd29307d18de846e089a3ff0b
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FDateHelper.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FDateHelper.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/DateHelper.java
import org.apache.log4j.Logger;

import org.apache.log4j.Logger;

@@ -1,13 +1,13 @@
 package gov.nysenate.openleg.util;
 
+import org.apache.log4j.Logger;
+
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Date;
 
-import org.apache.log4j.Logger;
-
 public class DateHelper
 {
     private static Logger logger = Logger.getLogger(DateHelper.class);
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FDateHelper.java
775a9a130fcb7471e6d59d1ed581d2d0c97022f3
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FJSPHelper.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FJSPHelper.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/JSPHelper.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.entity.Person;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
        /** FIXME
        }         */
        /** FIXME
        }        */
import gov.nysenate.openleg.model.Bill;
import gov.nysenate.openleg.model.Person;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;

        }
        }
@@ -1,16 +1,14 @@
 package gov.nysenate.openleg.util;
 
-import gov.nysenate.openleg.model.Bill;
-import gov.nysenate.openleg.model.Person;
+import gov.nysenate.openleg.model.bill.Bill;
+import gov.nysenate.openleg.model.entity.Person;
+import org.apache.commons.lang3.StringUtils;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.commons.lang3.StringUtils;
-
 public class JSPHelper
 {
     public static String getPersonLink(String person, HttpServletRequest request)
@@ -99,23 +97,25 @@ public static String getSponsorLinks(Bill bill, HttpServletRequest request)
     public static String getCoSponsorLinks(Bill bill, HttpServletRequest request)
     {
         ArrayList<String> links = new ArrayList<String>();
+        /** FIXME
         for (Person sponsor : bill.getCoSponsors()) {
             if (!bill.getOtherSponsors().contains(sponsor)) {
                 links.add(JSPHelper.getPersonLink(sponsor, request));
             }
-        }
+        }         */
 
         return StringUtils.join(links, ", ");
     }
 
     public static String getMultiSponsorLinks(Bill bill, HttpServletRequest request)
     {
         ArrayList<String> links = new ArrayList<String>();
+        /** FIXME
         for (Person sponsor : bill.getMultiSponsors()) {
             if (!bill.getOtherSponsors().contains(sponsor)) {
                 links.add(JSPHelper.getPersonLink(sponsor, request));
             }
-        }
+        }        */
         return StringUtils.join(links, ", ");
     }
 }
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FJSPHelper.java
775112681af8d3524e335234a5a42d7a9e39e3d5
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FRequestUtils.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FRequestUtils.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/RequestUtils.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequest;

@@ -1,12 +1,11 @@
 package gov.nysenate.openleg.util;
 
+import javax.servlet.http.HttpServletRequest;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.servlet.http.HttpServletRequest;
-
 public class RequestUtils {
     private static Pattern formatPattern = Pattern.compile("\\.(json|xml)$");
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FRequestUtils.java
80e3071c79362651a1ce803f09c39ef7dade13f2
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FResultIterator.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FResultIterator.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/ResultIterator.java
import org.apache.log4j.Logger;
//        try {
//            int start = (page-1)*max;
            //senateResponse = Application.getLucene().search(query, start, max, sortBy, reverse);
//            page++;
//        }
//        catch (IOException e) {
//            logger.error(e);
//        }
//        ApiHelper.buildSearchResultList(senateResponse);
import gov.nysenate.openleg.api.ApiHelper;
import java.io.IOException;
import org.apache.log4j.Logger;

        try {
            int start = (page-1)*max;
            senateResponse = Application.getLucene().search(query, start, max, sortBy, reverse);
            page++;
        }
        catch (IOException e) {
            logger.error(e);
        }
        ApiHelper.buildSearchResultList(senateResponse);
@@ -1,16 +1,13 @@
 package gov.nysenate.openleg.util;
 
-import gov.nysenate.openleg.api.ApiHelper;
 import gov.nysenate.openleg.model.Result;
 import gov.nysenate.openleg.model.SenateResponse;
+import org.apache.log4j.Logger;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
-import org.apache.log4j.Logger;
-
 public class ResultIterator implements Iterator<Result>, Iterable<Result> {
     private static Logger logger = Logger.getLogger(ResultIterator.class);
 
@@ -89,21 +86,21 @@ private void doQuery() {
             return;
         }
 
-        try {
-            int start = (page-1)*max;
-            senateResponse = Application.getLucene().search(query, start, max, sortBy, reverse);
-            page++;
-        }
-        catch (IOException e) {
-            logger.error(e);
-        }
+//        try {
+//            int start = (page-1)*max;
+            //senateResponse = Application.getLucene().search(query, start, max, sortBy, reverse);
+//            page++;
+//        }
+//        catch (IOException e) {
+//            logger.error(e);
+//        }
 
         if(senateResponse == null) {
             exhausted = true;
             return;
         }
 
-        ApiHelper.buildSearchResultList(senateResponse);
+//        ApiHelper.buildSearchResultList(senateResponse);
 
         ArrayList<Result> results = senateResponse.getResults();
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FResultIterator.java
d1f841453f10573d267c6510b54cbaf04ac8e381
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FStorage.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FStorage.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/Storage.java
import gov.nysenate.openleg.model.bill.Bill;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
    private static final Logger logger = Logger.getLogger(Storage.class);
//    private final StorageJsonConverter converter;
//        this.converter = new StorageJsonConverter(this);
//                try {
//                    if (cls == Bill.class) {
//                        value = this.converter.readBill(storageFile);
//                    }
//                    else if (cls == Calendar.class) {
//                        value = this.converter.readCalendar(storageFile);
//                    }
//                    else if (cls == Transcript.class) {
//                        value = this.converter.readTranscript(storageFile);
//                    }
//                    else {
//                        logger.error("Unable to read value of type "+cls.getName()+" from: "+storageFile);
//                        return null;
//                    }
//                } catch (org.codehaus.jackson.JsonParseException e) {
//                    logger.error("could not parse json", e);
//                } catch (JsonMappingException e) {
//                    logger.error("could not map json", e);
//                } catch (IOException e) {
//                    logger.debug("Storage Miss: "+storageFile);
//                }
//            }
//            else {
//                logger.debug("Missing key: "+key);
        return null; /** KILLED THIS */
        //return value.getYear()+"/"+value.getOtype()+"/"+value.getOid();
//            try {
//                FileUtils.forceMkdir(storageFile.getParentFile());
//                if (value instanceof Bill) {
//                    this.converter.write((Bill)value, storageFile);
//                }
//                else if (value instanceof Calendar) {
//                    this.converter.write((Calendar)value, storageFile);
//                }
//                else if (value instanceof Transcript) {
//                    this.converter.write((Transcript)value, storageFile);
//                }
//                else {
//                    logger.error("Unable to write value of type "+value.getClass().getName()+": "+value.getOid());
//                    return;
//                }
//            }
//            catch (IOException e) {
//                logger.error("Cannot open file for writing: "+storageFile, e);
//            }
cls == Calendar.class
src/main/java/gov/nysenate/openleg/util/Storage.java
//                        value = this.converter.readCalendar(storageFile);
//
else if (cls == Calendar.class)
cls == Transcript.class
src/main/java/gov/nysenate/openleg/util/Storage.java
//                        value = this.converter.readTranscript(storageFile);
//
else if (cls == Transcript.class)
value instanceof Calendar
src/main/java/gov/nysenate/openleg/util/Storage.java
//                    this.converter.write((Calendar)value, storageFile);
//
else if (value instanceof Calendar)
value instanceof Transcript
src/main/java/gov/nysenate/openleg/util/Storage.java
//                    this.converter.write((Transcript)value, storageFile);
//
else if (value instanceof Transcript)
import gov.nysenate.openleg.converter.StorageJsonConverter;
import gov.nysenate.openleg.model.Bill;
import gov.nysenate.openleg.model.Calendar;
import gov.nysenate.openleg.model.Transcript;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.JsonMappingException;

    public static void main(String[] args) {
        List<String> test = new ArrayList<String>();
        System.out.println(test.getClass().cast(test));
    }

    protected final Logger logger;
    private final StorageJsonConverter converter;
        this.logger  = Logger.getLogger(this.getClass());

        this.converter = new StorageJsonConverter(this);
                try {
                    if (cls == Bill.class) {
                        value = this.converter.readBill(storageFile);
                    }
                    else if (cls == Calendar.class) {
                        value = this.converter.readCalendar(storageFile);
                    }
                    else if (cls == Transcript.class) {
                        value = this.converter.readTranscript(storageFile);
                    }
                    else {
                        logger.error("Unable to read value of type "+cls.getName()+" from: "+storageFile);
                        return null;
                    }
                } catch (org.codehaus.jackson.JsonParseException e) {
                    logger.error("could not parse json", e);
                } catch (JsonMappingException e) {
                    logger.error("could not map json", e);
                } catch (IOException e) {
                    logger.debug("Storage Miss: "+storageFile);
                }
            }
            else {
                logger.debug("Missing key: "+key);
        return value.getYear()+"/"+value.getOtype()+"/"+value.getOid();
            try {
                FileUtils.forceMkdir(storageFile.getParentFile());
                if (value instanceof Bill) {
                    this.converter.write((Bill)value, storageFile);
                }
                else if (value instanceof Calendar) {
                    this.converter.write((Calendar)value, storageFile);
                }
                else if (value instanceof Transcript) {
                    this.converter.write((Transcript)value, storageFile);
                }
                else {
                    logger.error("Unable to write value of type "+value.getClass().getName()+": "+value.getOid());
                    return;
                }
            }
            catch (IOException e) {
                logger.error("Cannot open file for writing: "+storageFile, e);
            }
String[] args
src/main/java/gov/nysenate/openleg/util/Storage.java
List<String> test = new ArrayList<String>();
        System.out.println(test.getClass().cast(test));
void main(String[] args)
cls == Calendar.class
src/main/java/gov/nysenate/openleg/util/Storage.java
value = this.converter.readCalendar(storageFile);
else if (cls == Calendar.class)
cls == Transcript.class
src/main/java/gov/nysenate/openleg/util/Storage.java
value = this.converter.readTranscript(storageFile);
else if (cls == Transcript.class)
value instanceof Calendar
src/main/java/gov/nysenate/openleg/util/Storage.java
this.converter.write((Calendar)value, storageFile);
else if (value instanceof Calendar)
value instanceof Transcript
src/main/java/gov/nysenate/openleg/util/Storage.java
this.converter.write((Transcript)value, storageFile);
else if (value instanceof Transcript)
@@ -1,21 +1,13 @@
 package gov.nysenate.openleg.util;
 
-import gov.nysenate.openleg.converter.StorageJsonConverter;
 import gov.nysenate.openleg.model.BaseObject;
-import gov.nysenate.openleg.model.Bill;
-import gov.nysenate.openleg.model.Calendar;
-import gov.nysenate.openleg.model.Transcript;
+import gov.nysenate.openleg.model.bill.Bill;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
 
 import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.log4j.Logger;
-import org.codehaus.jackson.map.JsonMappingException;
 
 /**
  * Simple file backed key-value store with that supports both published and unpublished
@@ -25,13 +17,7 @@
  */
 public class Storage
 {
-
-    public static void main(String[] args) {
-        List<String> test = new ArrayList<String>();
-        System.out.println(test.getClass().cast(test));
-    }
-
-    protected final Logger logger;
+    private static final Logger logger = Logger.getLogger(Storage.class);
 
     /**
      * Represents the current status of a key in storage:
@@ -78,7 +64,7 @@ public static enum Status { NEW, MODIFIED, DELETED, UNMODIFIED, UNKNOWN };
      */
     protected HashSet<String> brandNew;
 
-    private final StorageJsonConverter converter;
+//    private final StorageJsonConverter converter;
 
 
     /**
@@ -98,8 +84,6 @@ public Storage(String storagePath)
      */
     public Storage(File storageDir)
     {
-        this.logger  = Logger.getLogger(this.getClass());
-
         this.storageDir = storageDir;
         this.publishedDir = new File(storageDir, "published");
         this.unpublishedDir = new File(storageDir, "unpublished");
@@ -108,7 +92,7 @@ public Storage(File storageDir)
         this.dirty   = new HashSet<String>();
         this.brandNew = new HashSet<String>();
 
-        this.converter = new StorageJsonConverter(this);
+//        this.converter = new StorageJsonConverter(this);
     }
 
     /**
@@ -130,32 +114,32 @@ public BaseObject get(String key, Class<? extends BaseObject> cls)
             logger.debug("Cache miss: "+key);
             File storageFile = getStorageFile(key);
             if (storageFile != null) {
-                try {
-                    if (cls == Bill.class) {
-                        value = this.converter.readBill(storageFile);
-                    }
-                    else if (cls == Calendar.class) {
-                        value = this.converter.readCalendar(storageFile);
-                    }
-                    else if (cls == Transcript.class) {
-                        value = this.converter.readTranscript(storageFile);
-                    }
-                    else {
-                        logger.error("Unable to read value of type "+cls.getName()+" from: "+storageFile);
-                        return null;
-                    }
+//                try {
+//                    if (cls == Bill.class) {
+//                        value = this.converter.readBill(storageFile);
+//                    }
+//                    else if (cls == Calendar.class) {
+//                        value = this.converter.readCalendar(storageFile);
+//                    }
+//                    else if (cls == Transcript.class) {
+//                        value = this.converter.readTranscript(storageFile);
+//                    }
+//                    else {
+//                        logger.error("Unable to read value of type "+cls.getName()+" from: "+storageFile);
+//                        return null;
+//                    }
 
                     value.setBrandNew(brandNew.contains(key));
-                } catch (org.codehaus.jackson.JsonParseException e) {
-                    logger.error("could not parse json", e);
-                } catch (JsonMappingException e) {
-                    logger.error("could not map json", e);
-                } catch (IOException e) {
-                    logger.debug("Storage Miss: "+storageFile);
-                }
-            }
-            else {
-                logger.debug("Missing key: "+key);
+//                } catch (org.codehaus.jackson.JsonParseException e) {
+//                    logger.error("could not parse json", e);
+//                } catch (JsonMappingException e) {
+//                    logger.error("could not map json", e);
+//                } catch (IOException e) {
+//                    logger.debug("Storage Miss: "+storageFile);
+//                }
+//            }
+//            else {
+//                logger.debug("Missing key: "+key);
             }
         }
         return value;
@@ -182,7 +166,8 @@ public void set(BaseObject value)
      */
     public String key(BaseObject value)
     {
-        return value.getYear()+"/"+value.getOtype()+"/"+value.getOid();
+        return null; /** KILLED THIS */
+        //return value.getYear()+"/"+value.getOtype()+"/"+value.getOid();
     }
 
     /**
@@ -263,25 +248,25 @@ public void flush(String key)
         if (value != null) {
             File storageFile = value.isPublished() ? getPublishedFile(key) : getUnpublishedFile(key);
 
-            try {
-                FileUtils.forceMkdir(storageFile.getParentFile());
-                if (value instanceof Bill) {
-                    this.converter.write((Bill)value, storageFile);
-                }
-                else if (value instanceof Calendar) {
-                    this.converter.write((Calendar)value, storageFile);
-                }
-                else if (value instanceof Transcript) {
-                    this.converter.write((Transcript)value, storageFile);
-                }
-                else {
-                    logger.error("Unable to write value of type "+value.getClass().getName()+": "+value.getOid());
-                    return;
-                }
-            }
-            catch (IOException e) {
-                logger.error("Cannot open file for writing: "+storageFile, e);
-            }
+//            try {
+//                FileUtils.forceMkdir(storageFile.getParentFile());
+//                if (value instanceof Bill) {
+//                    this.converter.write((Bill)value, storageFile);
+//                }
+//                else if (value instanceof Calendar) {
+//                    this.converter.write((Calendar)value, storageFile);
+//                }
+//                else if (value instanceof Transcript) {
+//                    this.converter.write((Transcript)value, storageFile);
+//                }
+//                else {
+//                    logger.error("Unable to write value of type "+value.getClass().getName()+": "+value.getOid());
+//                    return;
+//                }
+//            }
+//            catch (IOException e) {
+//                logger.error("Cannot open file for writing: "+storageFile, e);
+//            }
         }
 
         // Mark the key as clean by removing from the dirty set.
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FStorage.java
dd55f9c70ecbf7178248cd710596f176f500485a
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FTextFormatter.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FTextFormatter.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/TextFormatter.java
import gov.nysenate.openleg.model.bill.Bill;
import gov.nysenate.openleg.model.Bill;
@@ -1,6 +1,6 @@
 package gov.nysenate.openleg.util;
 
-import gov.nysenate.openleg.model.Bill;
+import gov.nysenate.openleg.model.bill.Bill;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FTextFormatter.java
99186a343411936c5eee2674056adf1d5b5d8f29
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FXmlHelper.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FXmlHelper.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/util/XmlHelper.java
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;

import java.io.File;
import java.io.IOException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
@@ -1,9 +1,10 @@
 package gov.nysenate.openleg.util;
 
 import gov.nysenate.util.Config;
-
-import java.io.File;
-import java.io.IOException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -12,11 +13,8 @@
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFactory;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
+import java.io.File;
+import java.io.IOException;
 
 public class XmlHelper
 {
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Futil%2FXmlHelper.java
bd8e4a97680b3bcbf412f32ca059e81aa6ab3116
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLActcaldate.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLActcaldate.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLActcaldate.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLActcaldate.java
b592ed0fb169c8b5ddce6ccbc924df99b50974e7
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLBill.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLBill.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLBill.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
@@ -8,12 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlSchemaType;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLBill.java
837489b56cf2999888c13047f114430ebff1d5ac
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCaldate.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCaldate.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLCaldate.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCaldate.java
606e75d68aeb9a79888f9aaec1a9065862e20ff4
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCalno.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCalno.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLCalno.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
@@ -8,13 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlSchemaType;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCalno.java
4d95430dd7fb4803082ea92ba173ed959c2ddab9
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCalnos.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCalnos.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLCalnos.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@@ -8,13 +8,9 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
+import javax.xml.bind.annotation.*;
 import java.util.ArrayList;
 import java.util.List;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLCalnos.java
583d7684d8205e8a9499fa09a4002ca66256db9d
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLMotiondate.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLMotiondate.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLMotiondate.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLMotiondate.java
1e9d4f4e64cb7f6c6e5b6ae02f82bad67043e216
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLReleasedate.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLReleasedate.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLReleasedate.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLReleasedate.java
41ef7b8c7c0e97b1f6e8beecc539011399afae3f
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLReleasetime.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLReleasetime.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLReleasetime.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLReleasetime.java
dd30cf8a30fba1d3cf0903bc12a96b37d68e060f
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSENATEDATA.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSENATEDATA.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSENATEDATA.java
import javax.xml.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
@@ -8,18 +8,11 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElements;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlSchemaType;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import java.util.ArrayList;
+import java.util.List;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSENATEDATA.java
c2d4de2420f02d8d56af682d9db98ca48aa3a243
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSection.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSection.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSection.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
@@ -8,13 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlSchemaType;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSection.java
a9db2197b48011cd998b56ea656d95088790641e
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSections.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSections.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSections.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@@ -8,13 +8,9 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
+import javax.xml.bind.annotation.*;
 import java.util.ArrayList;
 import java.util.List;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSections.java
50ca6594b2932c25874e4195ef385eaab841f9fd
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSencalendar.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSencalendar.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSencalendar.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
@@ -8,13 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlSchemaType;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSencalendar.java
35015d19cd5439d3b2adb7e9fa350a0a08fa76da
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSencalendaractive.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSencalendaractive.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSencalendaractive.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
@@ -8,13 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlSchemaType;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSencalendaractive.java
9744c5f95f35bdfd1fb54b99b7f72d74cfb6f107
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSequence.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSequence.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSequence.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSequence.java
ee714514c2b7acd0d58f4ff30ba665016fccca4c
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSponsor.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSponsor.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSponsor.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSponsor.java
66543cccbac5e99723ffcdbd15037a1b3b02d557
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSubbill.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSubbill.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSubbill.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSubbill.java
3659028379562ab027323e8ccf61df22673333b2
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSubsponsor.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSubsponsor.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSubsponsor.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSubsponsor.java
0461341fcdcb2c808aaa462e6a72931e6deb0fca
modified
https://github.com/nysenate/OpenLegislation/blob/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSupplemental.java
https://api.github.com/repos/nysenate/OpenLegislation/contents/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSupplemental.java?ref=f89655fbb32c1372837f17bb6f7ed81f7a32c654
src/main/java/gov/nysenate/openleg/xml/calendar/XMLSupplemental.java
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@@ -8,11 +8,7 @@
 
 package gov.nysenate.openleg.xml.calendar;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.*;
 
 
 /**
https://github.com/nysenate/OpenLegislation/raw/f89655fbb32c1372837f17bb6f7ed81f7a32c654/src%2Fmain%2Fjava%2Fgov%2Fnysenate%2Fopenleg%2Fxml%2Fcalendar%2FXMLSupplemental.java
4675538496e7ef2024582b2a5dfa550d49c68f57
modified
nysenate$openlegislation
String baseBillPrintNo
this.baseBillPrintNo = baseBillPrintNo;
void
void setBaseBillPrintNo(String baseBillPrintNo)
String oid
this.oid = oid;
    /**
     * @return - The date of the action.
     */
    public Date getDate() {
        return date;
void
void setOid(String oid)