private void recordTripUpdate(TripUpdate tripUpdate, DataCopier tuCopier, DataCopier stCopier)
      throws SQLException {
    PreparedStatement stmt = null;

    int updateId = getUpdateId();

    DataCopierRow tuRow = null;

    if (tuCopier != null) {
      tuRow = new DataCopierRow();
    } else {
      stmt = mStatements.get(STTRIPUPDATE);
    }

    if (tuRow == null) {
      stmt.setInt(1, updateId);
    } else {
      tuRow.add(updateId);
    }

    if (tripUpdate.hasTimestamp()) {
      if (tuRow == null) {
        stmt.setLong(2, tripUpdate.getTimestamp());
      } else {
        tuRow.add(tripUpdate.getTimestamp());
      }
    } else {
      if (tuRow == null) {
        stmt.setNull(2, Types.INTEGER);
      } else {
        tuRow.addNull();
      }
    }

    if (tripUpdate.hasTrip()) {
      TripDescriptor trip = tripUpdate.getTrip();

      if (trip.hasScheduleRelationship()) {
        if (tuRow == null) {
          stmt.setInt(3, trip.getScheduleRelationship().getNumber());
        } else {
          tuRow.add(trip.getScheduleRelationship().getNumber());
        }
      } else {
        if (tuRow == null) {
          stmt.setNull(3, Types.INTEGER);
        } else {
          tuRow.addNull();
        }
      }

      if (trip.hasStartDate()) {
        if (tuRow == null) {
          stmt.setString(4, trip.getStartDate());
        } else {
          tuRow.add(trip.getStartDate());
        }
      } else {
        if (tuRow == null) {
          stmt.setNull(4, Types.VARCHAR);
        } else {
          tuRow.addNull();
        }
      }

      if (trip.hasStartTime()) {
        if (tuRow == null) {
          stmt.setString(5, trip.getStartTime());
        } else {
          tuRow.add(trip.getStartTime());
        }
      } else {
        if (tuRow == null) {
          stmt.setNull(5, Types.VARCHAR);
        } else {
          tuRow.addNull();
        }
      }

      if (trip.hasTripId()) {
        if (tuRow == null) {
          stmt.setString(6, trip.getTripId());
        } else {
          tuRow.add(trip.getTripId());
        }
      } else {
        if (tuRow == null) {
          stmt.setNull(6, Types.VARCHAR);
        } else {
          tuRow.addNull();
        }
      }

      if (trip.hasRouteId()) {
        if (tuRow == null) {
          stmt.setString(7, trip.getRouteId());
        } else {
          tuRow.add(trip.getRouteId());
        }
      } else {
        if (tuRow == null) {
          stmt.setNull(7, Types.VARCHAR);
        } else {
          tuRow.addNull();
        }
      }
    } else {
      if (tuRow == null) {
        stmt.setNull(3, Types.INTEGER);
        stmt.setNull(4, Types.VARCHAR);
        stmt.setNull(5, Types.VARCHAR);
        stmt.setNull(6, Types.VARCHAR);
        stmt.setNull(7, Types.VARCHAR);
      } else {
        tuRow.addNull(5);
      }
    }

    if (tripUpdate.hasVehicle()) {
      VehicleDescriptor vd = tripUpdate.getVehicle();

      if (vd.hasId()) {
        if (tuRow == null) {
          stmt.setString(8, vd.getId());
        } else {
          tuRow.add(vd.getId());
        }
      } else {
        if (tuRow == null) {
          stmt.setNull(8, Types.INTEGER);
        } else {
          tuRow.addNull();
        }
      }

      if (vd.hasLabel()) {
        if (tuRow == null) {
          stmt.setString(9, vd.getLabel());
        } else {
          tuRow.add(vd.getLabel());
        }
      } else {
        if (tuRow == null) {
          stmt.setNull(9, Types.VARCHAR);
        } else {
          tuRow.addNull();
        }
      }

      if (vd.hasLicensePlate()) {
        if (tuRow == null) {
          stmt.setString(10, vd.getLicensePlate());
        } else {
          tuRow.add(vd.getLicensePlate());
        }
      } else {
        if (tuRow == null) {
          stmt.setNull(10, Types.VARCHAR);
        } else {
          tuRow.addNull();
        }
      }
    } else {
      if (tuRow == null) {
        stmt.setNull(8, Types.INTEGER);
        stmt.setNull(9, Types.VARCHAR);
        stmt.setNull(10, Types.VARCHAR);
      } else {
        tuRow.addNull(3);
      }
    }

    Date recorded = new Date();

    if (tuRow == null) {
      stmt.setInt(11, (int) (recorded.getTime() / 1000));
    } else {
      tuRow.add((int) (recorded.getTime() / 1000));
    }

    if (tuCopier == null) {
      stmt.addBatch();
    } else {
      tuCopier.add(tuRow);
    }

    if (stCopier == null) {
      stmt = mStatements.get(STTRIPUPDATE_STOPTIMEUPDATES);
    } else {
      stmt = null;
    }

    for (StopTimeUpdate stu : tripUpdate.getStopTimeUpdateList()) {
      DataCopierRow stRow = null;

      if (stmt == null) {
        stRow = new DataCopierRow();
      }

      if (stRow == null) {
        stmt.setInt(1, updateId);
      } else {
        stRow.add(updateId);
      }

      if (stu.hasArrival()) {
        StopTimeEvent ste = stu.getArrival();

        if (ste.hasTime()) {
          if (stRow == null) {
            stmt.setLong(2, ste.getTime());
          } else {
            stRow.add(ste.getTime());
          }
        } else {
          if (stRow == null) {
            stmt.setNull(2, Types.INTEGER);
          } else {
            stRow.addNull();
          }
        }

        if (ste.hasUncertainty()) {
          if (stRow == null) {
            stmt.setInt(3, ste.getUncertainty());
          } else {
            stRow.add(ste.getUncertainty());
          }
        } else {
          if (stRow == null) {
            stmt.setNull(3, Types.INTEGER);
          } else {
            stRow.addNull();
          }
        }

        if (ste.hasDelay()) {
          if (stRow == null) {
            stmt.setInt(4, ste.getDelay());
          } else {
            stRow.add(ste.getDelay());
          }
        } else {
          if (stRow == null) {
            stmt.setNull(4, Types.INTEGER);
          } else {
            stRow.addNull();
          }
        }
      } else {
        if (stRow == null) {
          stmt.setNull(2, Types.INTEGER);
          stmt.setNull(3, Types.INTEGER);
          stmt.setNull(4, Types.INTEGER);
        } else {
          stRow.addNull(3);
        }
      }

      if (stu.hasDeparture()) {
        StopTimeEvent ste = stu.getDeparture();

        if (ste.hasTime()) {
          if (stRow == null) {
            stmt.setLong(5, ste.getTime());
          } else {
            stRow.add(ste.getTime());
          }
        } else {
          if (stRow == null) {
            stmt.setNull(5, Types.INTEGER);
          } else {
            stRow.addNull();
          }
        }

        if (ste.hasUncertainty()) {
          if (stRow == null) {
            stmt.setInt(6, ste.getUncertainty());
          } else {
            stRow.add(ste.getUncertainty());
          }
        } else {
          if (stRow == null) {
            stmt.setNull(6, Types.INTEGER);
          } else {
            stRow.addNull();
          }
        }

        if (ste.hasDelay()) {
          if (stRow == null) {
            stmt.setInt(7, ste.getDelay());
          } else {
            stRow.add(ste.getDelay());
          }
        } else {
          if (stRow == null) {
            stmt.setNull(7, Types.INTEGER);
          } else {
            stRow.addNull();
          }
        }
      } else {
        if (stRow == null) {
          stmt.setNull(5, Types.INTEGER);
          stmt.setNull(6, Types.INTEGER);
          stmt.setNull(7, Types.INTEGER);
        } else {
          stRow.addNull(3);
        }
      }

      int srInt =
          stu.hasScheduleRelationship()
              ? stu.getScheduleRelationship().getNumber()
              : com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate
                  .ScheduleRelationship.SCHEDULED_VALUE;
      if (stRow == null) {
        stmt.setInt(8, srInt);
      } else {
        stRow.add(srInt);
      }

      if (stu.hasStopId()) {
        if (stRow == null) {
          stmt.setString(9, stu.getStopId());
        } else {
          stRow.add(stu.getStopId());
        }
      } else {
        if (stRow == null) {
          stmt.setNull(9, Types.VARCHAR);
        } else {
          stRow.addNull();
        }
      }

      int ssInt = stu.hasStopSequence() ? stu.getStopSequence() : -1;

      if (stRow == null) {
        stmt.setInt(10, ssInt);
      } else {
        stRow.add(ssInt);
      }

      if (stmt == null) {
        stCopier.add(stRow);
      } else {
        stmt.addBatch();
      }
    }
  }
  public void record(FeedMessage feedMessage) throws SQLException {

    int numAlerts = 0;
    int numTripUpdates = 0;
    int numVehiclePositions = 0;

    mOpenQueries = 0;

    for (FeedEntity entity : feedMessage.getEntityList()) {
      if (entity.hasAlert()) {
        numAlerts++;
      }

      if (entity.hasTripUpdate()) {
        numTripUpdates++;
      }

      if (entity.hasVehicle()) {
        numVehiclePositions++;
      }
    }

    boolean hasAlerts = numAlerts > 0;
    boolean hasTripUpdates = numTripUpdates > 0;
    boolean hasVehiclePositions = numVehiclePositions > 0;

    mLogger.info(
        String.format(
            "Entities: alerts=%d, updates=%d, positions=%d",
            numAlerts, numTripUpdates, numVehiclePositions));

    mLogger.info("Clearing tables...");

    if (hasAlerts) {
      clearAlertsData();
    }

    if (hasTripUpdates) {
      clearTripUpdatesData();
    }

    if (hasVehiclePositions) {
      clearVehiclePositionsData();
    }

    mLogger.info("Finished clearing tables");

    if (!hasAlerts && !hasTripUpdates && !hasVehiclePositions) {
      mLogger.info("Nothing to record");
      return;
    }

    boolean useCopy = mConnection instanceof BaseConnection;

    CopyManager cm = null;

    DataCopier tuCopier = null;
    DataCopier stCopier = null;
    DataCopier vpCopier = null;

    CopyIn tuCopyIn = null;
    CopyIn stCopyIn = null;
    CopyIn vpCopyIn = null;

    if (useCopy) {
      cm = new CopyManager((BaseConnection) mConnection);
      tuCopier = new DataCopier();
      stCopier = new DataCopier();
      vpCopier = new DataCopier();

      if (hasTripUpdates) {
        stCopyIn = cm.copyIn(COPY_TRIP_UPDATES_STOP_TIMES);
        mOpenQueries++;

        stCopier = new DataCopier(stCopyIn, COPY_SEPARATOR);
      } else if (hasVehiclePositions) {
        vpCopyIn = cm.copyIn(COPY_VEHICLE_POSITIONS);
        mOpenQueries++;

        vpCopier = new DataCopier(vpCopyIn, COPY_SEPARATOR);
      }
    }

    for (FeedEntity entity : feedMessage.getEntityList()) {
      if (entity.hasAlert()) {
        try {
          recordAlert(entity.getAlert());
        } catch (SQLException e) {
          mLogger.warning(getString(e));
        }
      }

      if (entity.hasTripUpdate()) {
        try {
          recordTripUpdate(entity.getTripUpdate(), tuCopier, stCopier);
        } catch (Exception e) {
          mLogger.warning(getString(e));
        }
      }

      if (entity.hasVehicle()) {
        try {
          recordVehicle(entity.getVehicle(), vpCopier);
        } catch (Exception e) {
          mLogger.warning(getString(e));
        }
      }
    }

    if (hasAlerts) {
      mLogger.info("Committing alerts... ");

      try {
        mStatements.get(STALERT).executeBatch();
        mStatements.get(STALERT_ENTITIES).executeBatch();
        mStatements.get(STALERT_TIMERANGES).executeBatch();
        mLogger.info("done");
      } catch (Exception e) {
        mLogger.warning(getString(e));
      }
    }

    if (hasTripUpdates) {
      mLogger.info("Committing trip updates... ");

      try {
        if (stCopier == null) {
          mStatements.get(STTRIPUPDATE_STOPTIMEUPDATES).executeBatch();
        } else if (stCopyIn == null && stCopier.size() > 0) {
          stCopyIn = cm.copyIn(COPY_TRIP_UPDATES_STOP_TIMES);
          mOpenQueries++;

          stCopier.write(stCopyIn, COPY_SEPARATOR);
        }
      } catch (SQLException e) {
        e.printStackTrace();
      }

      if (stCopyIn != null) {
        try {
          stCopyIn.endCopy();
          mOpenQueries--;
        } catch (Exception e) {
          mLogger.warning(getString(e));
        }
      }

      try {
        if (tuCopier == null) {
          mStatements.get(STTRIPUPDATE).executeBatch();
        } else if (tuCopyIn == null && tuCopier.size() > 0) {
          tuCopyIn = cm.copyIn(COPY_TRIP_UPDATES);
          mOpenQueries++;

          tuCopier.write(tuCopyIn, COPY_SEPARATOR);
        }
      } catch (SQLException e) {
        e.printStackTrace();
      }

      if (tuCopyIn != null) {
        try {
          tuCopyIn.endCopy();
          mOpenQueries--;
        } catch (Exception e) {
          mLogger.warning(getString(e));
        }
      }

      mLogger.info("done");
    }

    if (hasVehiclePositions) {
      System.err.print("Committing vehicle positions... ");

      try {
        if (vpCopier == null) {
          mStatements.get(STVEHICLE).executeBatch();
        } else if (vpCopyIn == null && vpCopier.size() > 0) {
          vpCopyIn = cm.copyIn(COPY_VEHICLE_POSITIONS);
          mOpenQueries++;
          vpCopier.write(vpCopyIn, COPY_SEPARATOR);
        }
      } catch (Exception e) {
        mLogger.warning(getString(e));
      }

      if (vpCopyIn != null) {
        vpCopyIn.endCopy();
        mOpenQueries--;
      }

      mLogger.info("done");
    }
  }
  private void recordVehicle(VehiclePosition vehicle, DataCopier copier)
      throws SQLException, Exception {
    if (!vehicle.hasPosition()) {
      throw new Exception("No position found");
    }

    PreparedStatement stmt = null;

    DataCopierRow row = null;

    if (copier == null) {
      stmt = mStatements.get(STVEHICLE);
    } else {
      row = new DataCopierRow();
    }

    int congestionLevel =
        vehicle.hasCongestionLevel()
            ? vehicle.getCongestionLevel().getNumber()
            : CongestionLevel.UNKNOWN_CONGESTION_LEVEL_VALUE;
    int vehicleStatus =
        vehicle.hasCurrentStatus()
            ? vehicle.getCurrentStatus().getNumber()
            : VehicleStopStatus.IN_TRANSIT_TO_VALUE;
    int stopSequence = vehicle.hasCurrentStopSequence() ? vehicle.getCurrentStopSequence() : -1;

    if (row == null) {
      stmt.setInt(1, congestionLevel);
      stmt.setInt(2, vehicleStatus);
      stmt.setInt(3, stopSequence);
    } else {
      row.add(congestionLevel);
      row.add(vehicleStatus);
      row.add(stopSequence);
    }

    Position pos = vehicle.getPosition();

    if (pos.hasBearing()) {
      if (row == null) {
        stmt.setFloat(4, pos.getBearing());
      } else {
        row.add(pos.getBearing());
      }
    } else {
      if (row == null) {
        stmt.setNull(4, Types.FLOAT);
      } else {
        row.addNull();
      }
    }

    if (pos.hasOdometer()) {
      if (row == null) {
        stmt.setDouble(5, pos.getOdometer());
      } else {
        row.add(pos.getOdometer());
      }
    } else {
      if (row == null) {
        stmt.setNull(5, Types.DOUBLE);
      } else {
        row.addNull();
      }
    }

    if (pos.hasSpeed()) {
      if (row == null) {
        stmt.setFloat(6, pos.getSpeed());
      } else {
        row.add(pos.getSpeed());
      }
    } else {
      if (row == null) {
        stmt.setNull(6, Types.FLOAT);
      } else {
        row.addNull();
      }
    }

    if (pos.hasLatitude()) {
      if (row == null) {
        stmt.setFloat(7, pos.getLatitude());
      } else {
        row.add(pos.getLatitude());
      }
    } else {
      if (row == null) {
        stmt.setNull(7, Types.FLOAT);
      } else {
        row.addNull();
      }
    }

    if (pos.hasLongitude()) {
      if (row == null) {
        stmt.setFloat(8, pos.getLongitude());
      } else {
        row.add(pos.getLongitude());
      }
    } else {
      if (row == null) {
        stmt.setNull(8, Types.FLOAT);
      } else {
        row.addNull();
      }
    }

    if (vehicle.hasStopId()) {
      if (row == null) {
        stmt.setString(9, vehicle.getStopId());
      } else {
        row.add(vehicle.getStopId());
      }
    } else {
      if (row == null) {
        stmt.setNull(9, Types.VARCHAR);
      } else {
        row.addNull();
      }
    }

    if (vehicle.hasTimestamp()) {
      if (row == null) {
        stmt.setLong(10, vehicle.getTimestamp());
      } else {
        row.add(vehicle.getTimestamp());
      }
    } else {
      if (row == null) {
        stmt.setNull(10, Types.INTEGER);
      } else {
        row.addNull();
      }
    }

    if (vehicle.hasTrip()) {
      TripDescriptor trip = vehicle.getTrip();

      if (trip.hasScheduleRelationship()) {
        if (row == null) {
          stmt.setInt(11, trip.getScheduleRelationship().getNumber());
        } else {
          row.add(trip.getScheduleRelationship().getNumber());
        }
      } else {
        if (row == null) {
          stmt.setNull(11, Types.INTEGER);
        } else {
          row.addNull();
        }
      }

      if (trip.hasStartDate()) {
        if (row == null) {
          stmt.setString(12, trip.getStartDate());
        } else {
          row.add(trip.getStartDate());
        }
      } else {
        if (row == null) {
          stmt.setNull(12, Types.VARCHAR);
        } else {
          row.addNull();
        }
      }

      if (trip.hasStartTime()) {
        if (row == null) {
          stmt.setString(13, trip.getStartTime());
        } else {
          row.add(trip.getStartTime());
        }
      } else {
        if (row == null) {
          stmt.setNull(13, Types.VARCHAR);
        } else {
          row.addNull();
        }
      }

      if (trip.hasTripId()) {
        if (row == null) {
          stmt.setString(14, trip.getTripId());
        } else {
          row.add(trip.getTripId());
        }
      } else {
        if (row == null) {
          stmt.setNull(14, Types.VARCHAR);
        } else {
          row.addNull();
        }
      }

      if (trip.hasRouteId()) {
        if (row == null) {
          stmt.setString(15, trip.getRouteId());
        } else {
          row.add(trip.getRouteId());
        }
      } else {
        if (row == null) {
          stmt.setNull(15, Types.VARCHAR);
        } else {
          row.addNull();
        }
      }
    } else {
      if (row == null) {
        stmt.setNull(11, Types.INTEGER);
        stmt.setNull(12, Types.VARCHAR);
        stmt.setNull(13, Types.VARCHAR);
        stmt.setNull(14, Types.VARCHAR);
        stmt.setNull(15, Types.VARCHAR);
      } else {
        row.addNull(5);
      }
    }

    if (vehicle.hasVehicle()) {
      VehicleDescriptor vd = vehicle.getVehicle();

      if (vd.hasId()) {
        if (row == null) {
          stmt.setString(16, vd.getId());
        } else {
          row.add(vd.getId());
        }
      } else {
        if (row == null) {
          stmt.setNull(16, Types.VARCHAR);
        } else {
          row.addNull();
        }
      }

      if (vd.hasLabel()) {
        if (row == null) {
          stmt.setString(17, vd.getLabel());
        } else {
          row.add(vd.getLabel());
        }
      } else {
        if (row == null) {
          stmt.setNull(17, Types.VARCHAR);
        } else {
          row.addNull();
        }
      }

      if (vd.hasLicensePlate()) {
        if (row == null) {
          stmt.setString(18, vd.getLicensePlate());
        } else {
          row.add(vd.getLicensePlate());
        }
      } else {
        if (row == null) {
          stmt.setNull(18, Types.VARCHAR);
        } else {
          row.addNull();
        }
      }
    } else {
      if (row == null) {
        stmt.setNull(16, Types.VARCHAR);
        stmt.setNull(17, Types.VARCHAR);
        stmt.setNull(18, Types.VARCHAR);
      } else {
        row.addNull(3);
      }
    }

    Date recorded = new Date();

    if (row == null) {
      stmt.setInt(19, (int) (recorded.getTime() / 1000));
    } else {
      row.add((int) (recorded.getTime() / 1000));
    }

    if (stmt == null) {
      copier.add(row);
    } else {
      stmt.execute();
    }
  }