signature:
public class CachedIndicator { /** * Increases the size of cached results buffer .
 * @ param index the index to increase length to
 * @ param maxLength the maximum length of the results buffer */
private void increaseLengthTo ( int index , int maxLength ) { } }

implementation:
if ( highestResultIndex > - 1 ) { int newResultsCount = Math . min ( index - highestResultIndex , maxLength ) ; if ( newResultsCount == maxLength ) { results . clear ( ) ; results . addAll ( Collections . < T > nCopies ( maxLength , null ) ) ; } else if ( newResultsCount > 0 ) { results . addAll ( Collections . < T > nCopies ( newResultsCount , null ) ) ; removeExceedingResults ( maxLength ) ; } } else { // First use of cache
assert results . isEmpty ( ) : "Cache results list should be empty" ; results . addAll ( Collections . < T > nCopies ( Math . min ( index + 1 , maxLength ) , null ) ) ; }


signature:
public class CachedIndicator { /** * Removes the N first results which exceed the maximum tick count .
 * ( i . e . keeps only the last maximumResultCount results )
 * @ param maximumResultCount the number of results to keep */
private void removeExceedingResults ( int maximumResultCount ) { } }

implementation:
int resultCount = results . size ( ) ; if ( resultCount > maximumResultCount ) { // Removing old results
final int nbResultsToRemove = resultCount - maximumResultCount ; for ( int i = 0 ; i < nbResultsToRemove ; i ++ ) { results . remove ( 0 ) ; } }


signature:
public class MaximumDrawdownCriterion { /** * Calculates the maximum drawdown from a cash flow over a series .
 * @ param series the time series
 * @ param cashFlow the cash flow
 * @ return the maximum drawdown from a cash flow over a series */
private Decimal calculateMaximumDrawdown ( TimeSeries series , CashFlow cashFlow ) { } }

implementation:
Decimal maximumDrawdown = Decimal . ZERO ; Decimal maxPeak = Decimal . ZERO ; if ( ! series . isEmpty ( ) ) { // The series is not empty
for ( int i = series . getBeginIndex ( ) ; i <= series . getEndIndex ( ) ; i ++ ) { Decimal value = cashFlow . getValue ( i ) ; if ( value . isGreaterThan ( maxPeak ) ) { maxPeak = value ; } Decimal drawdown = maxPeak . minus ( value ) . dividedBy ( maxPeak ) ; if ( drawdown . isGreaterThan ( maximumDrawdown ) ) { maximumDrawdown = drawdown ; } } } return maximumDrawdown ;


signature:
public class Decimal { /** * Returns a { @ code Decimal } whose value is { @ code ( this + augend ) } ,
 * with rounding according to the context settings .
 * @ param augend value to be added to this { @ code Decimal } .
 * @ return { @ code this + augend } , rounded as necessary
 * @ see BigDecimal # add ( java . math . BigDecimal , java . math . MathContext ) */
public Decimal plus ( Decimal augend ) { } }

implementation: if ( ( this == NaN ) || ( augend == NaN ) ) { return NaN ; } return new Decimal ( delegate . add ( augend . delegate , MATH_CONTEXT ) ) ;


signature:
public class Decimal { /** * Returns a { @ code Decimal } whose value is { @ code ( this - augend ) } ,
 * with rounding according to the context settings .
 * @ param subtrahend value to be subtracted from this { @ code Decimal } .
 * @ return { @ code this - subtrahend } , rounded as necessary
 * @ see BigDecimal # subtract ( java . math . BigDecimal , java . math . MathContext ) */
public Decimal minus ( Decimal subtrahend ) { } }

implementation: if ( ( this == NaN ) || ( subtrahend == NaN ) ) { return NaN ; } return new Decimal ( delegate . subtract ( subtrahend . delegate , MATH_CONTEXT ) ) ;


signature:
public class Decimal { /** * Returns a { @ code Decimal } whose value is { @ code this * multiplicand } ,
 * with rounding according to the context settings .
 * @ param multiplicand value to be multiplied by this { @ code Decimal } .
 * @ return { @ code this * multiplicand } , rounded as necessary
 * @ see BigDecimal # multiply ( java . math . BigDecimal , java . math . MathContext ) */
public Decimal multipliedBy ( Decimal multiplicand ) { } }

implementation: if ( ( this == NaN ) || ( multiplicand == NaN ) ) { return NaN ; } return new Decimal ( delegate . multiply ( multiplicand . delegate , MATH_CONTEXT ) ) ;


signature:
public class Decimal { /** * Returns a { @ code Decimal } whose value is { @ code ( this / divisor ) } ,
 * with rounding according to the context settings .
 * @ param divisor value by which this { @ code Decimal } is to be divided .
 * @ return { @ code this / divisor } , rounded as necessary
 * @ see BigDecimal # divide ( java . math . BigDecimal , java . math . MathContext ) */
public Decimal dividedBy ( Decimal divisor ) { } }

implementation: if ( ( this == NaN ) || ( divisor == NaN ) || divisor . isZero ( ) ) { return NaN ; } return new Decimal ( delegate . divide ( divisor . delegate , MATH_CONTEXT ) ) ;


signature:
public class Decimal { /** * Returns a { @ code Decimal } whose value is { @ code ( this % divisor ) } ,
 * with rounding according to the context settings .
 * @ param divisor value by which this { @ code Decimal } is to be divided .
 * @ return { @ code this % divisor } , rounded as necessary .
 * @ see BigDecimal # remainder ( java . math . BigDecimal , java . math . MathContext ) */
public Decimal remainder ( Decimal divisor ) { } }

implementation: if ( ( this == NaN ) || ( divisor == NaN ) || divisor . isZero ( ) ) { return NaN ; } return new Decimal ( delegate . remainder ( divisor . delegate , MATH_CONTEXT ) ) ;


signature:
public class Decimal { /** * Returns a { @ code Decimal } whose value is < tt > ( this < sup > n < / sup > ) < / tt > .
 * @ param n power to raise this { @ code Decimal } to .
 * @ return < tt > this < sup > n < / sup > < / tt >
 * @ see BigDecimal # pow ( int , java . math . MathContext ) */
public Decimal pow ( int n ) { } }

implementation: if ( this == NaN ) { return NaN ; } return new Decimal ( delegate . pow ( n , MATH_CONTEXT ) ) ;


signature:
public class Decimal { /** * Returns the minimum of this { @ code Decimal } and { @ code other } .
 * @ param other value with which the minimum is to be computed
 * @ return the { @ code Decimal } whose value is the lesser of this
 * { @ code Decimal } and { @ code other } . If they are equal ,
 * as defined by the { @ link # compareTo ( Decimal ) compareTo }
 * method , { @ code this } is returned . */
public Decimal min ( Decimal other ) { } }

implementation: if ( ( this == NaN ) || ( other == NaN ) ) { return NaN ; } return ( compareTo ( other ) <= 0 ? this : other ) ;


signature:
public class Decimal { /** * Returns the maximum of this { @ code Decimal } and { @ code other } .
 * @ param other value with which the maximum is to be computed
 * @ return the { @ code Decimal } whose value is the greater of this
 * { @ code Decimal } and { @ code other } . If they are equal ,
 * as defined by the { @ link # compareTo ( Decimal ) compareTo }
 * method , { @ code this } is returned . */
public Decimal max ( Decimal other ) { } }

implementation: if ( ( this == NaN ) || ( other == NaN ) ) { return NaN ; } return ( compareTo ( other ) >= 0 ? this : other ) ;


signature:
public class PeriodicalGrowthRateIndicator { /** * Gets the TotalReturn from the calculated results of the method ' calculate ' .
 * For a timeFrame = number of trading days within a year ( e . g . 251 days in the US )
 * and " end of day " - ticks you will get the ' Annualized Total Return ' .
 * Only complete timeFrames are taken into the calculation .
 * @ return the total return from the calculated results of the method ' calculate ' */
public double getTotalReturn ( ) { } }

implementation:
Decimal totalProduct = Decimal . ONE ; int completeTimeframes = ( getTimeSeries ( ) . getTickCount ( ) / timeFrame ) ; for ( int i = 1 ; i <= completeTimeframes ; i ++ ) { int index = i * timeFrame ; Decimal currentReturn = getValue ( index ) ; // Skip NaN at the end of a series
if ( currentReturn != Decimal . NaN ) { currentReturn = currentReturn . plus ( Decimal . ONE ) ; totalProduct = totalProduct . multipliedBy ( currentReturn ) ; } } return ( Math . pow ( totalProduct . toDouble ( ) , ( 1.0 / completeTimeframes ) ) ) ;


signature:
public class WalkForward { /** * Builds a list of split indexes from splitDuration .
 * @ param series the time series to get split begin indexes of
 * @ param splitDuration the duration between 2 splits
 * @ return a list of begin indexes after split */
public static List < Integer > getSplitBeginIndexes ( TimeSeries series , Duration splitDuration ) { } }

implementation:
ArrayList < Integer > beginIndexes = new ArrayList < > ( ) ; int beginIndex = series . getBeginIndex ( ) ; int endIndex = series . getEndIndex ( ) ; // Adding the first begin index
beginIndexes . add ( beginIndex ) ; // Building the first interval before next split
ZonedDateTime beginInterval = series . getFirstTick ( ) . getEndTime ( ) ; ZonedDateTime endInterval = beginInterval . plus ( splitDuration ) ; for ( int i = beginIndex ; i <= endIndex ; i ++ ) { // For each tick . . .
ZonedDateTime tickTime = series . getTick ( i ) . getEndTime ( ) ; if ( tickTime . isBefore ( beginInterval ) || ! tickTime . isBefore ( endInterval ) ) { // Tick out of the interval
if ( ! endInterval . isAfter ( tickTime ) ) { // Tick after the interval
// - - > Adding a new begin index
beginIndexes . add ( i ) ; } // Building the new interval before next split
beginInterval = endInterval . isBefore ( tickTime ) ? tickTime : endInterval ; endInterval = beginInterval . plus ( splitDuration ) ; } } return beginIndexes ;


signature:
public class WalkForward { /** * Returns a new time series which is a view of a subset of the current series .
 * The new series has begin and end indexes which correspond to the bounds of the sub - set into the full series . < br >
 * The tick of the series are shared between the original time series and the returned one ( i . e . no copy ) .
 * @ param series the time series to get a sub - series of
 * @ param beginIndex the begin index ( inclusive ) of the time series
 * @ param duration the duration of the time series
 * @ return a constrained { @ link TimeSeries time series } which is a sub - set of the current series */
public static TimeSeries subseries ( TimeSeries series , int beginIndex , Duration duration ) { } }

implementation:
// Calculating the sub - series interval
ZonedDateTime beginInterval = series . getTick ( beginIndex ) . getEndTime ( ) ; ZonedDateTime endInterval = beginInterval . plus ( duration ) ; // Checking ticks belonging to the sub - series ( starting at the provided index )
int subseriesNbTicks = 0 ; int endIndex = series . getEndIndex ( ) ; for ( int i = beginIndex ; i <= endIndex ; i ++ ) { // For each tick . . .
ZonedDateTime tickTime = series . getTick ( i ) . getEndTime ( ) ; if ( tickTime . isBefore ( beginInterval ) || ! tickTime . isBefore ( endInterval ) ) { // Tick out of the interval
break ; } // Tick in the interval
// - - > Incrementing the number of ticks in the subseries
subseriesNbTicks ++ ; } return new BaseTimeSeries ( series , beginIndex , beginIndex + subseriesNbTicks - 1 ) ;


signature:
public class WalkForward { /** * Splits the time series into sub - series lasting sliceDuration . < br >
 * The current time series is splitted every splitDuration . < br >
 * The last sub - series may last less than sliceDuration .
 * @ param series the time series to split
 * @ param splitDuration the duration between 2 splits
 * @ param sliceDuration the duration of each sub - series
 * @ return a list of sub - series */
public static List < TimeSeries > splitSeries ( TimeSeries series , Duration splitDuration , Duration sliceDuration ) { } }

implementation: ArrayList < TimeSeries > subseries = new ArrayList < > ( ) ; if ( splitDuration != null && ! splitDuration . isZero ( ) && sliceDuration != null && ! sliceDuration . isZero ( ) ) { List < Integer > beginIndexes = getSplitBeginIndexes ( series , splitDuration ) ; for ( Integer subseriesBegin : beginIndexes ) { subseries . add ( subseries ( series , subseriesBegin , sliceDuration ) ) ; } } return subseries ;


signature:
public class SimpleLinearRegressionIndicator { /** * Calculates the regression line .
 * @ param startIndex the start index ( inclusive ) in the time series
 * @ param endIndex the end index ( inclusive ) in the time series */
private void calculateRegressionLine ( int startIndex , int endIndex ) { } }

implementation:
// First pass : compute xBar and yBar
Decimal sumX = Decimal . ZERO ; Decimal sumY = Decimal . ZERO ; for ( int i = startIndex ; i <= endIndex ; i ++ ) { sumX = sumX . plus ( Decimal . valueOf ( i ) ) ; sumY = sumY . plus ( indicator . getValue ( i ) ) ; } Decimal nbObservations = Decimal . valueOf ( endIndex - startIndex + 1 ) ; Decimal xBar = sumX . dividedBy ( nbObservations ) ; Decimal yBar = sumY . dividedBy ( nbObservations ) ; // Second pass : compute slope and intercept
Decimal xxBar = Decimal . ZERO ; Decimal xyBar = Decimal . ZERO ; for ( int i = startIndex ; i <= endIndex ; i ++ ) { Decimal dX = Decimal . valueOf ( i ) . minus ( xBar ) ; Decimal dY = indicator . getValue ( i ) . minus ( yBar ) ; xxBar = xxBar . plus ( dX . multipliedBy ( dX ) ) ; xyBar = xyBar . plus ( dX . multipliedBy ( dY ) ) ; } slope = xyBar . dividedBy ( xxBar ) ; intercept = yBar . minus ( slope . multipliedBy ( xBar ) ) ;


signature:
public class Trade { /** * Operates the trade at the index - th position
 * @ param index the tick index
 * @ param price the price
 * @ param amount the amount
 * @ return the order */
public Order operate ( int index , Decimal price , Decimal amount ) { } }

implementation: Order order = null ; if ( isNew ( ) ) { order = new Order ( index , startingType , price , amount ) ; entry = order ; } else if ( isOpened ( ) ) { if ( index < entry . getIndex ( ) ) { throw new IllegalStateException ( "The index i is less than the entryOrder index" ) ; } order = new Order ( index , startingType . complementType ( ) , price , amount ) ; exit = order ; } return order ;


signature:
public class BuyAndSellSignalsToChart { /** * Builds a JFreeChart time series from a Ta4j time series and an indicator .
 * @ param tickSeries the ta4j time series
 * @ param indicator the indicator
 * @ param name the name of the chart time series
 * @ return the JFreeChart time series */
private static org . jfree . data . time . TimeSeries buildChartTimeSeries ( TimeSeries tickSeries , Indicator < Decimal > indicator , String name ) { } }

implementation: org . jfree . data . time . TimeSeries chartTimeSeries = new org . jfree . data . time . TimeSeries ( name ) ; for ( int i = 0 ; i < tickSeries . getTickCount ( ) ; i ++ ) { Tick tick = tickSeries . getTick ( i ) ; chartTimeSeries . add ( new Minute ( Date . from ( tick . getEndTime ( ) . toInstant ( ) ) ) , indicator . getValue ( i ) . toDouble ( ) ) ; } return chartTimeSeries ;


signature:
public class BuyAndSellSignalsToChart { /** * Runs a strategy over a time series and adds the value markers
 * corresponding to buy / sell signals to the plot .
 * @ param series a time series
 * @ param strategy a trading strategy
 * @ param plot the plot */
private static void addBuySellSignals ( TimeSeries series , Strategy strategy , XYPlot plot ) { } }

implementation:
// Running the strategy
TimeSeriesManager seriesManager = new TimeSeriesManager ( series ) ; List < Trade > trades = seriesManager . run ( strategy ) . getTrades ( ) ; // Adding markers to plot
for ( Trade trade : trades ) { // Buy signal
double buySignalTickTime = new Minute ( Date . from ( series . getTick ( trade . getEntry ( ) . getIndex ( ) ) . getEndTime ( ) . toInstant ( ) ) ) . getFirstMillisecond ( ) ; Marker buyMarker = new ValueMarker ( buySignalTickTime ) ; buyMarker . setPaint ( Color . GREEN ) ; buyMarker . setLabel ( "B" ) ; plot . addDomainMarker ( buyMarker ) ; // Sell signal
double sellSignalTickTime = new Minute ( Date . from ( series . getTick ( trade . getExit ( ) . getIndex ( ) ) . getEndTime ( ) . toInstant ( ) ) ) . getFirstMillisecond ( ) ; Marker sellMarker = new ValueMarker ( sellSignalTickTime ) ; sellMarker . setPaint ( Color . RED ) ; sellMarker . setLabel ( "S" ) ; plot . addDomainMarker ( sellMarker ) ; }


signature:
public class TradingBotOnMovingTimeSeries { /** * Builds a moving time series ( i . e . keeping only the maxTickCount last ticks )
 * @ param maxTickCount the number of ticks to keep in the time series ( at maximum )
 * @ return a moving time series */
private static TimeSeries initMovingTimeSeries ( int maxTickCount ) { } }

implementation:
TimeSeries series = CsvTradesLoader . loadBitstampSeries ( ) ; System . out . print ( "Initial tick count: " + series . getTickCount ( ) ) ; // Limitating the number of ticks to maxTickCount
series . setMaximumTickCount ( maxTickCount ) ; LAST_TICK_CLOSE_PRICE = series . getTick ( series . getEndIndex ( ) ) . getClosePrice ( ) ; System . out . println ( " (limited to " + maxTickCount + "), close price = " + LAST_TICK_CLOSE_PRICE ) ; return series ;


signature:
public class TradingBotOnMovingTimeSeries { /** * Generates a random decimal number between min and max .
 * @ param min the minimum bound
 * @ param max the maximum bound
 * @ return a random decimal number between min and max */
private static Decimal randDecimal ( Decimal min , Decimal max ) { } }

implementation: Decimal randomDecimal = null ; if ( min != null && max != null && min . isLessThan ( max ) ) { randomDecimal = max . minus ( min ) . multipliedBy ( Decimal . valueOf ( Math . random ( ) ) ) . plus ( min ) ; } return randomDecimal ;


signature:
public class TradingBotOnMovingTimeSeries { /** * Generates a random tick .
 * @ return a random tick */
private static Tick generateRandomTick ( ) { } }

implementation:
final Decimal maxRange = Decimal . valueOf ( "0.03" ) ; // 3.0%
Decimal openPrice = LAST_TICK_CLOSE_PRICE ; Decimal minPrice = openPrice . minus ( openPrice . multipliedBy ( maxRange . multipliedBy ( Decimal . valueOf ( Math . random ( ) ) ) ) ) ; Decimal maxPrice = openPrice . plus ( openPrice . multipliedBy ( maxRange . multipliedBy ( Decimal . valueOf ( Math . random ( ) ) ) ) ) ; Decimal closePrice = randDecimal ( minPrice , maxPrice ) ; LAST_TICK_CLOSE_PRICE = closePrice ; return new BaseTick ( ZonedDateTime . now ( ) , openPrice , maxPrice , minPrice , closePrice , Decimal . ONE ) ;


signature:
public class TotalProfitCriterion { /** * Calculates the profit of a trade ( Buy and sell ) .
 * @ param series a time series
 * @ param trade a trade
 * @ return the profit of the trade */
private double calculateProfit ( TimeSeries series , Trade trade ) { } }

implementation: Decimal profit = Decimal . ONE ; if ( trade . isClosed ( ) ) { Decimal exitClosePrice = series . getTick ( trade . getExit ( ) . getIndex ( ) ) . getClosePrice ( ) ; Decimal entryClosePrice = series . getTick ( trade . getEntry ( ) . getIndex ( ) ) . getClosePrice ( ) ; if ( trade . getEntry ( ) . isBuy ( ) ) { profit = exitClosePrice . dividedBy ( entryClosePrice ) ; } else { profit = entryClosePrice . dividedBy ( exitClosePrice ) ; } } return profit . toDouble ( ) ;


signature:
public class ParabolicSarIndicator { /** * Increments the acceleration factor . */
private void incrementAcceleration ( ) { } }

implementation: if ( acceleration . isGreaterThanOrEqual ( ACCELERATION_THRESHOLD ) ) { acceleration = MAX_ACCELERATION ; } else { acceleration = acceleration . plus ( ACCELERATION_INCREMENT ) ; }


signature:
public class ParabolicSarIndicator { /** * Calculates the SAR .
 * @ param index the tick index
 * @ return the SAR */
private Decimal calculateSar ( int index ) { } }

implementation: Decimal previousSar = getValue ( index - 1 ) ; return extremePoint . multipliedBy ( acceleration ) . plus ( Decimal . ONE . minus ( acceleration ) . multipliedBy ( previousSar ) ) ;


signature:
public class CashFlow { /** * Calculates the cash flow for a single trade .
 * @ param trade a single trade */
private void calculate ( Trade trade ) { } }

implementation: final int entryIndex = trade . getEntry ( ) . getIndex ( ) ; int begin = entryIndex + 1 ; if ( begin > values . size ( ) ) { Decimal lastValue = values . get ( values . size ( ) - 1 ) ; values . addAll ( Collections . nCopies ( begin - values . size ( ) , lastValue ) ) ; } int end = trade . getExit ( ) . getIndex ( ) ; for ( int i = Math . max ( begin , 1 ) ; i <= end ; i ++ ) { Decimal ratio ; if ( trade . getEntry ( ) . isBuy ( ) ) { ratio = timeSeries . getTick ( i ) . getClosePrice ( ) . dividedBy ( timeSeries . getTick ( entryIndex ) . getClosePrice ( ) ) ; } else { ratio = timeSeries . getTick ( entryIndex ) . getClosePrice ( ) . dividedBy ( timeSeries . getTick ( i ) . getClosePrice ( ) ) ; } values . add ( values . get ( entryIndex ) . multipliedBy ( ratio ) ) ; }


signature:
public class CashFlow { /** * Fills with last value till the end of the series . */
private void fillToTheEnd ( ) { } }

implementation: if ( timeSeries . getEndIndex ( ) >= values . size ( ) ) { Decimal lastValue = values . get ( values . size ( ) - 1 ) ; values . addAll ( Collections . nCopies ( timeSeries . getEndIndex ( ) - values . size ( ) + 1 , lastValue ) ) ; }


signature:
public class StrategyExecutionLogging { /** * Loads the Logback configuration from a resource file .
 * Only here to avoid polluting other examples with logs . Could be replaced by a simple logback . xml file in the resource folder . */
private static void loadLoggerConfiguration ( ) { } }

implementation: LoggerContext context = ( LoggerContext ) LoggerFactory . getILoggerFactory ( ) ; context . reset ( ) ; JoranConfigurator configurator = new JoranConfigurator ( ) ; configurator . setContext ( context ) ; try { configurator . doConfigure ( LOGBACK_CONF_FILE ) ; } catch ( JoranException je ) { Logger . getLogger ( StrategyExecutionLogging . class . getName ( ) ) . log ( Level . SEVERE , "Unable to load Logback configuration" , je ) ; }


signature:
public class TrailingStopLossIndicator { /** * Simple implementation of the trailing stop - loss concept .
 * Logic :
 * IF CurrentPrice - StopLossDistance > StopLossLimit THEN StopLossLimit = CurrentPrice - StopLossDistance
 * @ param index
 * @ return Decimal */
@ Override protected Decimal calculate ( int index ) { } }

implementation:
if ( stopLossLimit . isNaN ( ) ) { // Case without initial stop - loss limit value
stopLossLimit = indicator . getValue ( 0 ) . minus ( stopLossDistance ) ; } Decimal currentValue = indicator . getValue ( index ) ; Decimal referenceValue = stopLossLimit . plus ( stopLossDistance ) ; if ( currentValue . isGreaterThan ( referenceValue ) ) { stopLossLimit = currentValue . minus ( stopLossDistance ) ; } return stopLossLimit ;


signature:
public class BaseTradingRecord { /** * Records an order and the corresponding trade ( if closed ) .
 * @ param order the order to be recorded
 * @ param isEntry true if the order is an entry , false otherwise ( exit ) */
private void recordOrder ( Order order , boolean isEntry ) { } }

implementation:
if ( order == null ) { throw new IllegalArgumentException ( "Order should not be null" ) ; } // Storing the new order in entries / exits lists
if ( isEntry ) { entryOrders . add ( order ) ; } else { exitOrders . add ( order ) ; } // Storing the new order in orders list
orders . add ( order ) ; if ( OrderType . BUY . equals ( order . getType ( ) ) ) { // Storing the new order in buy orders list
buyOrders . add ( order ) ; } else if ( OrderType . SELL . equals ( order . getType ( ) ) ) { // Storing the new order in sell orders list
sellOrders . add ( order ) ; } // Storing the trade if closed
if ( currentTrade . isClosed ( ) ) { trades . add ( currentTrade ) ; currentTrade = new Trade ( startingType ) ; }


signature:
public class TimeSeriesManager { /** * Runs the provided strategy over the managed series ( from startIndex to finishIndex ) .
 * Opens the trades with { @ link OrderType . BUY } orders .
 * @ param strategy the trading strategy
 * @ param orderType the { @ link OrderType } used to open the trades
 * @ param startIndex the start index for the run ( included )
 * @ param finishIndex the finish index for the run ( included )
 * @ return the trading record coming from the run */
public TradingRecord run ( Strategy strategy , OrderType orderType , int startIndex , int finishIndex ) { } }

implementation: return run ( strategy , orderType , Decimal . NaN , startIndex , finishIndex ) ;


signature:
public class TimeSeriesManager { /** * Runs the provided strategy over the managed series .
 * @ param strategy the trading strategy
 * @ param orderType the { @ link OrderType } used to open the trades
 * @ param amount the amount used to open / close the trades
 * @ return the trading record coming from the run */
public TradingRecord run ( Strategy strategy , OrderType orderType , Decimal amount ) { } }

implementation: return run ( strategy , orderType , amount , timeSeries . getBeginIndex ( ) , timeSeries . getEndIndex ( ) ) ;


signature:
public class TimeSeriesManager { /** * Runs the provided strategy over the managed series ( from startIndex to finishIndex ) .
 * @ param strategy the trading strategy
 * @ param orderType the { @ link OrderType } used to open the trades
 * @ param amount the amount used to open / close the trades
 * @ param startIndex the start index for the run ( included )
 * @ param finishIndex the finish index for the run ( included )
 * @ return the trading record coming from the run */
public TradingRecord run ( Strategy strategy , OrderType orderType , Decimal amount , int startIndex , int finishIndex ) { } }

implementation:
int runBeginIndex = Math . max ( startIndex , timeSeries . getBeginIndex ( ) ) ; int runEndIndex = Math . min ( finishIndex , timeSeries . getEndIndex ( ) ) ; log . trace ( "Running strategy (indexes: {} -> {}): {} (starting with {})" , runBeginIndex , runEndIndex , strategy , orderType ) ; TradingRecord tradingRecord = new BaseTradingRecord ( orderType ) ; for ( int i = runBeginIndex ; i <= runEndIndex ; i ++ ) { // For each tick between both indexes . . .
if ( strategy . shouldOperate ( i , tradingRecord ) ) { tradingRecord . operate ( i , timeSeries . getTick ( i ) . getClosePrice ( ) , amount ) ; } } if ( ! tradingRecord . isClosed ( ) ) { // If the last trade is still opened , we search out of the run end index .
// May works if the end index for this run was inferior to the actual number of ticks
int seriesMaxSize = Math . max ( timeSeries . getEndIndex ( ) + 1 , timeSeries . getTickData ( ) . size ( ) ) ; for ( int i = runEndIndex + 1 ; i < seriesMaxSize ; i ++ ) { // For each tick after the end index of this run . . .
// - - > Trying to close the last trade
if ( strategy . shouldOperate ( i , tradingRecord ) ) { tradingRecord . operate ( i , timeSeries . getTick ( i ) . getClosePrice ( ) , amount ) ; break ; } } } return tradingRecord ;


signature:
public class CandlestickChart { /** * Builds a JFreeChart OHLC dataset from a ta4j time series .
 * @ param series a time series
 * @ return an Open - High - Low - Close dataset */
private static OHLCDataset createOHLCDataset ( TimeSeries series ) { } }

implementation: final int nbTicks = series . getTickCount ( ) ; Date [ ] dates = new Date [ nbTicks ] ; double [ ] opens = new double [ nbTicks ] ; double [ ] highs = new double [ nbTicks ] ; double [ ] lows = new double [ nbTicks ] ; double [ ] closes = new double [ nbTicks ] ; double [ ] volumes = new double [ nbTicks ] ; for ( int i = 0 ; i < nbTicks ; i ++ ) { Tick tick = series . getTick ( i ) ; dates [ i ] = new Date ( tick . getEndTime ( ) . toEpochSecond ( ) * 1000 ) ; opens [ i ] = tick . getOpenPrice ( ) . toDouble ( ) ; highs [ i ] = tick . getMaxPrice ( ) . toDouble ( ) ; lows [ i ] = tick . getMinPrice ( ) . toDouble ( ) ; closes [ i ] = tick . getClosePrice ( ) . toDouble ( ) ; volumes [ i ] = tick . getVolume ( ) . toDouble ( ) ; } OHLCDataset dataset = new DefaultHighLowDataset ( "btc" , dates , highs , lows , opens , closes , volumes ) ; return dataset ;


signature:
public class CandlestickChart { /** * Builds an additional JFreeChart dataset from a ta4j time series .
 * @ param series a time series
 * @ return an additional dataset */
private static TimeSeriesCollection createAdditionalDataset ( TimeSeries series ) { } }

implementation: ClosePriceIndicator indicator = new ClosePriceIndicator ( series ) ; TimeSeriesCollection dataset = new TimeSeriesCollection ( ) ; org . jfree . data . time . TimeSeries chartTimeSeries = new org . jfree . data . time . TimeSeries ( "Btc price" ) ; for ( int i = 0 ; i < series . getTickCount ( ) ; i ++ ) { Tick tick = series . getTick ( i ) ; chartTimeSeries . add ( new Second ( new Date ( tick . getEndTime ( ) . toEpochSecond ( ) * 1000 ) ) , indicator . getValue ( i ) . toDouble ( ) ) ; } dataset . addSeries ( chartTimeSeries ) ; return dataset ;


signature:
public class CashFlowToChart { /** * Adds the cash flow axis to the plot .
 * @ param plot the plot
 * @ param dataset the cash flow dataset */
private static void addCashFlowAxis ( XYPlot plot , TimeSeriesCollection dataset ) { } }

implementation: final NumberAxis cashAxis = new NumberAxis ( "Cash Flow Ratio" ) ; cashAxis . setAutoRangeIncludesZero ( false ) ; plot . setRangeAxis ( 1 , cashAxis ) ; plot . setDataset ( 1 , dataset ) ; plot . mapDatasetToRangeAxis ( 1 , 1 ) ; final StandardXYItemRenderer cashFlowRenderer = new StandardXYItemRenderer ( ) ; cashFlowRenderer . setSeriesPaint ( 0 , Color . blue ) ; plot . setRenderer ( 1 , cashFlowRenderer ) ;


signature:
public class CashFlowToChart { /** * Displays a chart in a frame .
 * @ param chart the chart to be displayed */
private static void displayChart ( JFreeChart chart ) { } }

implementation:
// Chart panel
ChartPanel panel = new ChartPanel ( chart ) ; panel . setFillZoomRectangle ( true ) ; panel . setMouseWheelEnabled ( true ) ; panel . setPreferredSize ( new Dimension ( 1024 , 400 ) ) ; // Application frame
ApplicationFrame frame = new ApplicationFrame ( "Ta4j example - Cash flow to chart" ) ; frame . setContentPane ( panel ) ; frame . pack ( ) ; RefineryUtilities . centerFrameOnScreen ( frame ) ; frame . setVisible ( true ) ;


signature:
public class BaseTimeSeries { /** * Removes the N first ticks which exceed the maximum tick count . */
private void removeExceedingTicks ( ) { } }

implementation:
int tickCount = ticks . size ( ) ; if ( tickCount > maximumTickCount ) { // Removing old ticks
int nbTicksToRemove = tickCount - maximumTickCount ; for ( int i = 0 ; i < nbTicksToRemove ; i ++ ) { ticks . remove ( 0 ) ; } // Updating removed ticks count
removedTicksCount += nbTicksToRemove ; }


signature:
public class CsvTradesLoader { /** * Builds a list of empty ticks .
 * @ param beginTime the begin time of the whole period
 * @ param endTime the end time of the whole period
 * @ param duration the tick duration ( in seconds )
 * @ return the list of empty ticks */
private static List < Tick > buildEmptyTicks ( ZonedDateTime beginTime , ZonedDateTime endTime , int duration ) { } }

implementation: List < Tick > emptyTicks = new ArrayList < > ( ) ; Duration tickDuration = Duration . ofSeconds ( duration ) ; ZonedDateTime tickEndTime = beginTime ; do { tickEndTime = tickEndTime . plus ( tickDuration ) ; emptyTicks . add ( new BaseTick ( tickDuration , tickEndTime ) ) ; } while ( tickEndTime . isBefore ( endTime ) ) ; return emptyTicks ;


signature:
public class CsvTradesLoader { /** * Removes all empty ( i . e . with no trade ) ticks of the list .
 * @ param ticks a list of ticks */
private static void removeEmptyTicks ( List < Tick > ticks ) { } }

implementation: for ( int i = ticks . size ( ) - 1 ; i >= 0 ; i -- ) { if ( ticks . get ( i ) . getTrades ( ) == 0 ) { ticks . remove ( i ) ; } }


signature:
public class ResubscribeHelper { /** * Initializes all helper classes in { @ code target } class hierarchy . */
static void initializeAutoTaggingAndResubscription ( Object target , ObservableGroup group ) { } }

implementation: Class < ? > cls = target . getClass ( ) ; String clsName = cls . getName ( ) ; while ( cls != null && ! clsName . startsWith ( "android." ) && ! clsName . startsWith ( "java." ) ) { initializeAutoTaggingAndResubscriptionInTargetClassOnly ( target , cls , group ) ; cls = cls . getSuperclass ( ) ; if ( cls != null ) { clsName = cls . getName ( ) ; } }


signature:
public class ObservableGroup { /** * Unlike { @ link # initializeAutoTaggingAndResubscription ( Object ) } superclasses will
 * < b > not < / b > be initialized ; only the fields in { @ code targetClass } will be initialized .
 * Sets a unique tag on all Observer fields of { @ code target }
 * that are annotated with { @ link AutoTag } or { @ link AutoResubscribe } .
 * Subscribe all Observer fields on the { @ code target }
 * that are annotated with { @ link AutoResubscribe }
 * and that have their corresponding Observable in flight .
 * Resubscribes all Observer fields on the target that are annotated with
 * { @ link AutoResubscribe } and that have their corresponding Observable in flight . */
public < T > void initializeAutoTaggingAndResubscriptionInTargetClassOnly ( T target , Class < T > targetClass ) { } }

implementation: Preconditions . checkNotNull ( target , "Target cannot be null" ) ; ResubscribeHelper . initializeAutoTaggingAndResubscriptionInTargetClassOnly ( target , targetClass , this ) ;


signature:
public class ObservableGroup { /** * Adds an { @ link Observable } and { @ link Observer } to this group and subscribes to it . If an
 * { @ link Observable } with the same tag is already added , the previous one will be canceled and
 * removed before adding and subscribing to the new one . */
< T > ManagedObservable < T > add ( final String observerTag , final String observableTag , Observable < T > observable , ObservableEmitter < ? super T > observer ) { } }

implementation: checkNotDestroyed ( ) ; final Map < String , ManagedObservable < ? > > existingObservables = getObservablesForObserver ( observerTag ) ; ManagedObservable < ? > previousObservable = existingObservables . get ( observableTag ) ; if ( previousObservable != null ) { cancelAndRemove ( observerTag , observableTag ) ; } ManagedObservable < T > managedObservable = new ManagedObservable < > ( observerTag , observableTag , observable , observer , new Action ( ) { @ Override public void run ( ) { existingObservables . remove ( observableTag ) ; } } ) ; existingObservables . put ( observableTag , managedObservable ) ; if ( ! locked ) { managedObservable . unlock ( ) ; } return managedObservable ;


signature:
public class ObservableGroup { /** * Transforms an existing { @ link Observable } by returning a new { @ link Observable } that is
 * automatically added to this { @ link ObservableGroup } with the provided { @ code tag } when
 * subscribed to . */
public < T > ObservableTransformer < ? super T , T > transform ( Observer < ? super T > observer , String observableTag ) { } }

implementation: return new GroupSubscriptionTransformer < > ( this , Utils . getObserverTag ( observer ) , observableTag ) ;


signature:
public class ObservableGroup { /** * Transforms an existing { @ link Observable } by returning a new { @ link Observable } that is
 * automatically added to this { @ link ObservableGroup } .
 * < p > Convenience method
 * for { @ link # transform ( Observer , String ) } when { @ code observer } only
 * is subscribed to one { @ link Observable } . */
public < T > ObservableTransformer < ? super T , T > transform ( Observer < ? super T > observer ) { } }

implementation: return transform ( observer , Utils . getObserverTag ( observer ) ) ;


signature:
public class ObservableGroup { /** * Cancels all subscriptions and releases references to Observables and Observers . No more
 * Observables can be added to this group after it has been destroyed and it becomes unusable . */
void destroy ( ) { } }

implementation: destroyed = true ; for ( Map < String , ManagedObservable < ? > > observableMap : groupMap . values ( ) ) { for ( ManagedObservable < ? > managedObservable : observableMap . values ( ) ) { managedObservable . cancel ( ) ; } observableMap . clear ( ) ; } groupMap . clear ( ) ;


signature:
public class ObservableGroup { /** * Locks ( prevents ) Observables added to this group from emitting new events . Observables added
 * via { @ link # transform ( Observer , String ) } while the group is locked will
 * * * not * * be subscribed until their respective group is unlocked . If it ' s never unlocked , then
 * the Observable will never be subscribed to at all . This does not clear references to existing
 * Observers . Please use { @ link # dispose ( ) } if you want to clear references to existing
 * Observers . */
public void lock ( ) { } }

implementation: locked = true ; forAllObservables ( new Consumer < ManagedObservable < ? > > ( ) { @ Override public void accept ( ManagedObservable < ? > managedObservable ) { managedObservable . lock ( ) ; } } ) ;


signature:
public class ObservableGroup { /** * Unlocks ( releases ) Observables added to this group to emit new events until they are locked ,
 * unsubscribed or cancelled . */
public void unlock ( ) { } }

implementation: locked = false ; forAllObservables ( new Consumer < ManagedObservable < ? > > ( ) { @ Override public void accept ( ManagedObservable < ? > managedObservable ) { managedObservable . unlock ( ) ; } } ) ;


signature:
public class ObservableGroup { /** * Disposes all Observables managed by this group . Also clears any references to existing
 * { @ link Observer } objects in order to avoid leaks . This does not disconnect from the upstream
 * Observable , so it can be resumed upon calling { @ link # observable ( Observer ) } if
 * needed . */
public void dispose ( ) { } }

implementation: forAllObservables ( new Consumer < ManagedObservable < ? > > ( ) { @ Override public void accept ( ManagedObservable < ? > managedObservable ) { managedObservable . dispose ( ) ; } } ) ;


signature:
public class ObservableGroup { /** * Returns an existing { @ link Observable } for the { @ link TaggedObserver } .
 * < p > Does not change the locked status of this { @ link ObservableGroup } .
 * If it is unlocked , and the Observable has already emitted events ,
 * they will be immediately delivered . If it is locked then no events will be
 * delivered until it is unlocked . */
public < T > Observable < T > observable ( Observer < ? super T > observer ) { } }

implementation: return observable ( observer , Utils . getObserverTag ( observer ) ) ;


signature:
public class ObservableGroup { /** * Resubscribes the { @ link TaggedObserver } to the observable identified by { @ code observableTag } . */
public < T > void resubscribe ( TaggedObserver < ? super T > observer , String observableTag ) { } }

implementation: final Observable < T > observable = observable ( observer , observableTag ) ; if ( observable != null ) { observable . subscribe ( observer ) ; }


signature:
public class ObservableGroup { /** * Resubscribes the { @ link TaggedObserver } to the observable
 * identified by { @ code observableTag } .
 * < p > Convenience method
 * for { @ link # resubscribe ( TaggedObserver , String ) } when { @ code observer } only
 * is subscribed to one { @ link Observable } . */
public < T > void resubscribe ( TaggedObserver < ? super T > observer ) { } }

implementation: resubscribe ( observer , Utils . getObserverTag ( observer ) ) ;


signature:
public class ObservableGroup { /** * Removes the { @ link Observable } identified by { @ code observableTag } for the given
 * { @ link Observer } and cancels it subscription .
 * No more events will be delivered to its subscriber .
 * < p > If no Observable is found for the provided { @ code observableTag } , nothing happens . */
public void cancelAndRemove ( Observer < ? > observer , String observableTag ) { } }

implementation: cancelAndRemove ( Utils . getObserverTag ( observer ) , observableTag ) ;


signature:
public class ObservableGroup { /** * Removes the supplied { @ link Observable } from this group and cancels it subscription . No more
 * events will be delivered to its subscriber . */
private void cancelAndRemove ( String observerTag , String observableTag ) { } }

implementation: Map < String , ManagedObservable < ? > > observables = getObservablesForObserver ( observerTag ) ; ManagedObservable < ? > managedObservable = observables . get ( observableTag ) ; if ( managedObservable != null ) { managedObservable . cancel ( ) ; observables . remove ( observableTag ) ; }


signature:
public class GroupLifecycleManager { /** * Call this method from your Activity or Fragment ' s onCreate method */
public static GroupLifecycleManager onCreate ( ObservableManager observableManager , @ Nullable Bundle savedState , @ Nullable Object target ) { } }

implementation:
ObservableGroup group ; if ( savedState != null ) { State state = savedState . getParcelable ( KEY_STATE ) ; Preconditions . checkState ( state != null , "Must call onSaveInstanceState() first" ) ; // First check the instance ID before restoring state . If it ' s not the same instance ,
// then we have to create a new group since the previous one is already destroyed .
// Android can sometimes reuse the same instance after saving state and we can ' t reliably
// determine when that happens . This is a workaround for that behavior .
if ( state . managerId != observableManager . id ( ) ) { group = observableManager . newGroup ( ) ; } else { group = observableManager . getGroup ( state . groupId ) ; } } else { group = observableManager . newGroup ( ) ; } group . lock ( ) ; GroupLifecycleManager manager = new GroupLifecycleManager ( observableManager , group ) ; if ( target != null ) { manager . initializeAutoTaggingAndResubscription ( target ) ; } return manager ;


signature:
public class GroupLifecycleManager { /** * Calls { @ link ObservableGroup # transform ( Observer ) } for the group managed by
 * this instance . */
public < T > ObservableTransformer < ? super T , T > transform ( Observer < ? super T > observer ) { } }

implementation: return group . transform ( observer ) ;


signature:
public class GroupLifecycleManager { /** * Calls { @ link ObservableGroup # transform ( Observer , String ) } for the group managed by
 * this instance . */
public < T > ObservableTransformer < ? super T , T > transform ( Observer < ? super T > observer , String observableTag ) { } }

implementation: return group . transform ( observer , observableTag ) ;


signature:
public class GroupLifecycleManager { /** * Calls
 * { @ link ObservableGroup # initializeAutoTaggingAndResubscriptionInTargetClassOnly ( Object , Class ) }
 * for the group managed by this instance . */
public < T > void initializeAutoTaggingAndResubscriptionInTargetClassOnly ( T target , Class < T > targetClass ) { } }

implementation: group . initializeAutoTaggingAndResubscriptionInTargetClassOnly ( target , targetClass ) ;


signature:
public class GroupLifecycleManager { /** * Call this method from your Activity or Fragment ' s onDestroy method */
public void onDestroy ( @ Nullable Activity activity ) { } }

implementation:
// We need to track whether the current Activity is finishing or not in order to decide if we
// should destroy the ObservableGroup . If the Activity is not finishing , then we should not
// destroy it , since we know that we ' re probably restoring state at some point and reattaching
// to the existing ObservableGroup . If saveState ( ) was not called , then it ' s likely that we ' re
// being destroyed and are not ever coming back . However , this isn ' t perfect , especially
// when using fragments in a ViewPager . We might want to allow users to explicitly destroy it
// instead , in order to mitigate this issue .
// Also in some situations it seems like an Activity can be recreated even though its
// isFinishing ( ) property is set to true . For this reason , we must also check
// isChangingConfigurations ( ) to make sure it ' s safe to destroy the group .
onDestroy ( ! hasSavedState || ( activity != null && activity . isFinishing ( ) && ! activity . isChangingConfigurations ( ) ) ) ;


signature:
public class GroupLifecycleManager { /** * Call this method from your Activity or Fragment ' s onSaveInstanceState method */
public void onSaveInstanceState ( Bundle outState ) { } }

implementation: hasSavedState = true ; outState . putParcelable ( KEY_STATE , new State ( observableManager . id ( ) , group . id ( ) ) ) ;


signature:
public class AbstractWatchKey { /** * WatchEvent not WatchEvent . Kind */
final void signalEvent ( WatchEvent . Kind < Path > kind , Path context ) { } }

implementation: post ( new Event < > ( kind , 1 , context ) ) ; signal ( ) ;


signature:
public class DirectoryWatcher { /** * Asynchronously watch the directories .
 * @ param executor the executor to use to watch asynchronously */
public CompletableFuture < Void > watchAsync ( Executor executor ) { } }

implementation: return CompletableFuture . supplyAsync ( ( ) -> { watch ( ) ; return null ; } , executor ) ;


signature:
public class DirectoryWatcher { /** * Watch the directories . Block until either the listener stops watching or the DirectoryWatcher
 * is closed . */
public void watch ( ) { } }

implementation:
for ( ; ; ) { if ( ! listener . isWatching ( ) ) { return ; } // wait for key to be signalled
WatchKey key ; try { key = watchService . take ( ) ; } catch ( InterruptedException x ) { return ; } for ( WatchEvent < ? > event : key . pollEvents ( ) ) { try { WatchEvent . Kind < ? > kind = event . kind ( ) ; // Context for directory entry event is the file name of entry
WatchEvent < Path > ev = PathUtils . cast ( event ) ; int count = ev . count ( ) ; Path eventPath = ev . context ( ) ; if ( ! keyRoots . containsKey ( key ) ) { throw new IllegalStateException ( "WatchService returned key [" + key + "] but it was not found in keyRoots!" ) ; } Path childPath = eventPath == null ? null : keyRoots . get ( key ) . resolve ( eventPath ) ; logger . debug ( "{} [{}]" , kind , childPath ) ; /* * If a directory is created , and we ' re watching recursively , then register it and its sub - directories . */
if ( kind == OVERFLOW ) { listener . onEvent ( new DirectoryChangeEvent ( EventType . OVERFLOW , childPath , count ) ) ; } else if ( eventPath == null ) { throw new IllegalStateException ( "WatchService returned a null path for " + kind . name ( ) ) ; } else if ( kind == ENTRY_CREATE ) { if ( Files . isDirectory ( childPath , NOFOLLOW_LINKS ) ) { if ( ! Boolean . TRUE . equals ( fileTreeSupported ) ) { registerAll ( childPath ) ; } /* * Our custom Mac service sends subdirectory changes but the Windows / Linux do not .
 * Walk the file tree to make sure we send create events for any files that were created . */
if ( ! isMac ) { PathUtils . recursiveVisitFiles ( childPath , dir -> notifyCreateEvent ( dir , count ) , file -> notifyCreateEvent ( file , count ) ) ; } } notifyCreateEvent ( childPath , count ) ; } else if ( kind == ENTRY_MODIFY ) { if ( fileHasher != null || Files . isDirectory ( childPath ) ) { /* * Note that existingHash may be null due to the file being created before we start listening
 * It ' s important we don ' t discard the event in this case */
HashCode existingHash = pathHashes . get ( childPath ) ; /* * newHash can be null when using File # delete ( ) on windows - it generates MODIFY and DELETE in succession .
 * In this case the MODIFY event can be safely ignored */
HashCode newHash = PathUtils . hash ( fileHasher , childPath ) ; if ( newHash != null && ! newHash . equals ( existingHash ) ) { pathHashes . put ( childPath , newHash ) ; listener . onEvent ( new DirectoryChangeEvent ( EventType . MODIFY , childPath , count ) ) ; } else if ( newHash == null ) { logger . debug ( "Failed to hash modified file [{}]. It may have been deleted." , childPath ) ; } } else { listener . onEvent ( new DirectoryChangeEvent ( EventType . MODIFY , childPath , count ) ) ; } } else if ( kind == ENTRY_DELETE ) { // we cannot tell if the deletion was on file or folder because path points nowhere
// ( file / folder was deleted )
pathHashes . entrySet ( ) . removeIf ( e -> e . getKey ( ) . startsWith ( childPath ) ) ; listener . onEvent ( new DirectoryChangeEvent ( EventType . DELETE , childPath , count ) ) ; } } catch ( Exception e ) { logger . debug ( "DirectoryWatcher got an exception while watching!" , e ) ; listener . onException ( e ) ; } } boolean valid = key . reset ( ) ; if ( ! valid ) { logger . debug ( "WatchKey for [{}] no longer valid; removing." , key . watchable ( ) ) ; // remove the key from the keyRoots
keyRoots . remove ( key ) ; // if there are no more keys left to watch , we can break out
if ( keyRoots . isEmpty ( ) ) { logger . debug ( "No more directories left to watch; terminating watcher." ) ; break ; } } }


signature:
public class DirectoryWatcher { /** * Internal method to be used by registerAll */
private void register ( Path directory , boolean useFileTreeModifier ) throws IOException { } }

implementation: logger . debug ( "Registering [{}]." , directory ) ; Watchable watchable = isMac ? new WatchablePath ( directory ) : directory ; WatchEvent . Modifier [ ] modifiers = useFileTreeModifier ? new WatchEvent . Modifier [ ] { ExtendedWatchEventModifier . FILE_TREE } : new WatchEvent . Modifier [ ] { } ; WatchEvent . Kind < ? > [ ] kinds = new WatchEvent . Kind < ? > [ ] { ENTRY_CREATE , ENTRY_DELETE , ENTRY_MODIFY } ; WatchKey watchKey = watchable . register ( watchService , kinds , modifiers ) ; keyRoots . put ( watchKey , directory ) ;


signature:
public class ExecJavaMojo { /** * Execute goal .
 * @ throws MojoExecutionException execution of the main class or one of the threads it generated failed .
 * @ throws MojoFailureException something bad happened . . . */
public void execute ( ) throws MojoExecutionException , MojoFailureException { } }

implementation:
if ( isSkip ( ) ) { getLog ( ) . info ( "skipping execute as per configuration" ) ; return ; } if ( killAfter != - 1 ) { getLog ( ) . warn ( "Warning: killAfter is now deprecated. Do you need it ? Please comment on MEXEC-6." ) ; } if ( null == arguments ) { arguments = new String [ 0 ] ; } if ( getLog ( ) . isDebugEnabled ( ) ) { StringBuffer msg = new StringBuffer ( "Invoking : " ) ; msg . append ( mainClass ) ; msg . append ( ".main(" ) ; for ( int i = 0 ; i < arguments . length ; i ++ ) { if ( i > 0 ) { msg . append ( ", " ) ; } msg . append ( arguments [ i ] ) ; } msg . append ( ")" ) ; getLog ( ) . debug ( msg ) ; } IsolatedThreadGroup threadGroup = new IsolatedThreadGroup ( mainClass /* name */
) ; Thread bootstrapThread = new Thread ( threadGroup , new Runnable ( ) { public void run ( ) { int sepIndex = mainClass . indexOf ( '/' ) ; final String bootClassName ; if ( sepIndex >= 0 ) { bootClassName = mainClass . substring ( sepIndex + 1 ) ; } else { bootClassName = mainClass ; } try { Class < ? > bootClass = Thread . currentThread ( ) . getContextClassLoader ( ) . loadClass ( bootClassName ) ; MethodHandles . Lookup lookup = MethodHandles . lookup ( ) ; MethodHandle mainHandle = lookup . findStatic ( bootClass , "main" , MethodType . methodType ( void . class , String [ ] . class ) ) ; mainHandle . invoke ( arguments ) ; } catch ( IllegalAccessException e ) { // just pass it on
Thread . currentThread ( ) . getThreadGroup ( ) . uncaughtException ( Thread . currentThread ( ) , new Exception ( "The specified mainClass doesn't contain a main method with appropriate signature." , e ) ) ; } catch ( InvocationTargetException e ) { // use the cause if available to improve the plugin execution output
Throwable exceptionToReport = e . getCause ( ) != null ? e . getCause ( ) : e ; Thread . currentThread ( ) . getThreadGroup ( ) . uncaughtException ( Thread . currentThread ( ) , exceptionToReport ) ; } catch ( Throwable e ) { // just pass it on
Thread . currentThread ( ) . getThreadGroup ( ) . uncaughtException ( Thread . currentThread ( ) , e ) ; } } } , mainClass + ".main()" ) ; bootstrapThread . setContextClassLoader ( getClassLoader ( ) ) ; setSystemProperties ( ) ; bootstrapThread . start ( ) ; joinNonDaemonThreads ( threadGroup ) ; // It ' s plausible that spontaneously a non - daemon thread might be created as we try and shut down ,
// but it ' s too late since the termination condition ( only daemon threads ) has been triggered .
if ( keepAlive ) { getLog ( ) . warn ( "Warning: keepAlive is now deprecated and obsolete. Do you need it? Please comment on MEXEC-6." ) ; waitFor ( 0 ) ; } if ( cleanupDaemonThreads ) { terminateThreads ( threadGroup ) ; try { threadGroup . destroy ( ) ; } catch ( IllegalThreadStateException e ) { getLog ( ) . warn ( "Couldn't destroy threadgroup " + threadGroup , e ) ; } } if ( originalSystemProperties != null ) { System . setProperties ( originalSystemProperties ) ; } synchronized ( threadGroup ) { if ( threadGroup . uncaughtException != null ) { throw new MojoExecutionException ( "An exception occured while executing the Java class. " + threadGroup . uncaughtException . getMessage ( ) , threadGroup . uncaughtException ) ; } } registerSourceRoots ( ) ;


signature:
public class ExecJavaMojo { /** * Set up a classloader for the execution of the main class .
 * @ return the classloader
 * @ throws MojoExecutionException if a problem happens */
private ClassLoader getClassLoader ( ) throws MojoExecutionException { } }

implementation: List < Path > classpathURLs = new ArrayList < > ( ) ; this . addRelevantPluginDependenciesToClasspath ( classpathURLs ) ; this . addRelevantProjectDependenciesToClasspath ( classpathURLs ) ; this . addAdditionalClasspathElements ( classpathURLs ) ; try { return LoaderFinder . find ( classpathURLs , mainClass ) ; } catch ( NullPointerException | IOException e ) { throw new MojoExecutionException ( e . getMessage ( ) , e ) ; }


signature:
public class ExecJavaMojo { /** * Add any relevant project dependencies to the classpath . Indirectly takes includePluginDependencies and
 * ExecutableDependency into consideration .
 * @ param path classpath of { @ link java . net . URL } objects
 * @ throws MojoExecutionException if a problem happens */
private void addRelevantPluginDependenciesToClasspath ( List < Path > path ) throws MojoExecutionException { } }

implementation: if ( hasCommandlineArgs ( ) ) { arguments = parseCommandlineArgs ( ) ; } for ( Artifact classPathElement : this . determineRelevantPluginDependencies ( ) ) { getLog ( ) . debug ( "Adding plugin dependency artifact: " + classPathElement . getArtifactId ( ) + " to classpath" ) ; path . add ( classPathElement . getFile ( ) . toPath ( ) ) ; }


signature:
public class ExecJavaMojo { /** * Add any relevant project dependencies to the classpath . Takes includeProjectDependencies into consideration .
 * @ param path classpath of { @ link java . net . URL } objects
 * @ throws MojoExecutionException if a problem happens */
private void addRelevantProjectDependenciesToClasspath ( List < Path > path ) throws MojoExecutionException { } }

implementation: if ( this . includeProjectDependencies ) { getLog ( ) . debug ( "Project Dependencies will be included." ) ; List < Artifact > artifacts = new ArrayList < > ( ) ; List < Path > theClasspathFiles = new ArrayList < > ( ) ; collectProjectArtifactsAndClasspath ( artifacts , theClasspathFiles ) ; for ( Path classpathFile : theClasspathFiles ) { getLog ( ) . debug ( "Adding to classpath : " + classpathFile ) ; path . add ( classpathFile ) ; } for ( Artifact classPathElement : artifacts ) { getLog ( ) . debug ( "Adding project dependency artifact: " + classPathElement . getArtifactId ( ) + " to classpath" ) ; path . add ( classPathElement . getFile ( ) . toPath ( ) ) ; } } else { getLog ( ) . debug ( "Project Dependencies will be excluded." ) ; }


signature:
public class ExecJavaMojo { /** * Resolve the executable dependencies for the specified project
 * @ param executablePomArtifact the project ' s POM
 * @ return a set of Artifacts
 * @ throws MojoExecutionException if a failure happens */
private Set < Artifact > resolveExecutableDependencies ( Artifact executablePomArtifact ) throws MojoExecutionException { } }

implementation: Set < Artifact > executableDependencies = new LinkedHashSet < > ( ) ; try { ProjectBuildingRequest buildingRequest = getSession ( ) . getProjectBuildingRequest ( ) ; MavenProject executableProject = this . projectBuilder . build ( executablePomArtifact , buildingRequest ) . getProject ( ) ; for ( ArtifactResult artifactResult : dependencyResolver . resolveDependencies ( buildingRequest , executableProject . getModel ( ) , null ) ) { executableDependencies . add ( artifactResult . getArtifact ( ) ) ; } } catch ( Exception ex ) { throw new MojoExecutionException ( "Encountered problems resolving dependencies of the executable " + "in preparation for its execution." , ex ) ; } return executableDependencies ;


signature:
public class ExecutableDependency { /** * Matches the groupId and artifactId .
 * @ param artifact a { @ link org . apache . maven . artifact . Artifact } object .
 * @ return < code > true < / code > if both math , < code > false < / code > otherwise . */
public boolean matches ( Artifact artifact ) { } }

implementation: return artifact . getGroupId ( ) . equals ( this . getGroupId ( ) ) && artifact . getArtifactId ( ) . equals ( this . getArtifactId ( ) ) ;


signature:
public class AbstractExecMojo { /** * Collects the project artifacts in the specified List and the project specific classpath ( build output and build
 * test output ) Files in the specified List , depending on the plugin classpathScope value .
 * @ param artifacts the list where to collect the scope specific artifacts
 * @ param theClasspathFiles the list where to collect the scope specific output directories */
protected void collectProjectArtifactsAndClasspath ( List < Artifact > artifacts , List < Path > theClasspathFiles ) { } }

implementation: if ( addResourcesToClasspath ) { for ( Resource r : project . getBuild ( ) . getResources ( ) ) { theClasspathFiles . add ( Paths . get ( r . getDirectory ( ) ) ) ; } } if ( "compile" . equals ( classpathScope ) ) { artifacts . addAll ( project . getCompileArtifacts ( ) ) ; if ( addOutputToClasspath ) { theClasspathFiles . add ( Paths . get ( project . getBuild ( ) . getOutputDirectory ( ) ) ) ; } } else if ( "test" . equals ( classpathScope ) ) { artifacts . addAll ( project . getTestArtifacts ( ) ) ; if ( addOutputToClasspath ) { theClasspathFiles . add ( Paths . get ( project . getBuild ( ) . getTestOutputDirectory ( ) ) ) ; theClasspathFiles . add ( Paths . get ( project . getBuild ( ) . getOutputDirectory ( ) ) ) ; } } else if ( "runtime" . equals ( classpathScope ) ) { artifacts . addAll ( project . getRuntimeArtifacts ( ) ) ; if ( addOutputToClasspath ) { theClasspathFiles . add ( Paths . get ( project . getBuild ( ) . getOutputDirectory ( ) ) ) ; } } else if ( "system" . equals ( classpathScope ) ) { artifacts . addAll ( project . getSystemArtifacts ( ) ) ; } else { throw new IllegalStateException ( "Invalid classpath scope: " + classpathScope ) ; } getLog ( ) . debug ( "Collected project artifacts " + artifacts ) ; getLog ( ) . debug ( "Collected project classpath " + theClasspathFiles ) ;


signature:
public class AbstractExecMojo { /** * Examine the plugin dependencies to find the executable artifact .
 * @ return an artifact which refers to the actual executable tool ( not a POM )
 * @ throws MojoExecutionException if no executable artifact was found */
protected Artifact findExecutableArtifact ( ) throws MojoExecutionException { } }

implementation:
// ILimitedArtifactIdentifier execToolAssembly = this . getExecutableToolAssembly ( ) ;
Artifact executableTool = null ; for ( Artifact pluginDep : this . pluginDependencies ) { if ( this . executableDependency . matches ( pluginDep ) ) { executableTool = pluginDep ; break ; } } if ( executableTool == null ) { throw new MojoExecutionException ( "No dependency of the plugin matches the specified executableDependency." + "  Specified executableToolAssembly is: " + executableDependency . toString ( ) ) ; } return executableTool ;


signature:
public class ExecMojo { /** * priority in the execute method will be to use System properties arguments over the pom specification .
 * @ throws MojoExecutionException if a failure happens */
public void execute ( ) throws MojoExecutionException { } }

implementation: if ( executable == null ) { if ( executableDependency == null ) { throw new MojoExecutionException ( "The parameter 'executable' is missing or invalid" ) ; } executable = findExecutableArtifact ( ) . getFile ( ) . getAbsolutePath ( ) ; getLog ( ) . debug ( "using executable dependency " + executable ) ; } if ( isSkip ( ) ) { getLog ( ) . info ( "skipping execute as per configuration" ) ; return ; } if ( basedir == null ) { throw new IllegalStateException ( "basedir is null. Should not be possible." ) ; } try { handleWorkingDirectory ( ) ; String argsProp = getSystemProperty ( "exec.args" ) ; List < String > commandArguments = new ArrayList < String > ( ) ; if ( hasCommandlineArgs ( ) ) { handleCommandLineArgs ( commandArguments ) ; } else if ( ! StringUtils . isEmpty ( argsProp ) ) { handleSystemPropertyArguments ( argsProp , commandArguments ) ; } else { if ( arguments != null ) { handleArguments ( commandArguments ) ; } } Map < String , String > enviro = handleSystemEnvVariables ( ) ; CommandLine commandLine = getExecutablePath ( enviro , workingDirectory ) ; String [ ] args = commandArguments . toArray ( new String [ commandArguments . size ( ) ] ) ; commandLine . addArguments ( args , false ) ; Executor exec = new DefaultExecutor ( ) ; exec . setWorkingDirectory ( workingDirectory ) ; fillSuccessCodes ( exec ) ; getLog ( ) . debug ( "Executing command line: " + commandLine ) ; try { int resultCode ; if ( outputFile != null ) { if ( ! outputFile . getParentFile ( ) . exists ( ) && ! outputFile . getParentFile ( ) . mkdirs ( ) ) { getLog ( ) . warn ( "Could not create non existing parent directories for log file: " + outputFile ) ; } FileOutputStream outputStream = null ; try { outputStream = new FileOutputStream ( outputFile ) ; resultCode = executeCommandLine ( exec , commandLine , enviro , outputStream ) ; } finally { IOUtil . close ( outputStream ) ; } } else { resultCode = executeCommandLine ( exec , commandLine , enviro , System . out , System . err ) ; } if ( isResultCodeAFailure ( resultCode ) ) { String message = "Result of " + commandLine . toString ( ) + " execution is: '" + resultCode + "'." ; getLog ( ) . error ( message ) ; throw new MojoExecutionException ( message ) ; } } catch ( ExecuteException e ) { getLog ( ) . error ( "Command execution failed." , e ) ; throw new MojoExecutionException ( "Command execution failed." , e ) ; } catch ( IOException e ) { getLog ( ) . error ( "Command execution failed." , e ) ; throw new MojoExecutionException ( "Command execution failed." , e ) ; } registerSourceRoots ( ) ; } catch ( IOException e ) { throw new MojoExecutionException ( "I/O Error" , e ) ; }


signature:
public class ExecMojo { /** * This is a convenient method to make the execute method a little bit more readable . It will define the
 * workingDirectory to be the baseDir in case of workingDirectory is null . If the workingDirectory does not exist it
 * will created .
 * @ throws MojoExecutionException */
private void handleWorkingDirectory ( ) throws MojoExecutionException { } }

implementation: if ( workingDirectory == null ) { workingDirectory = basedir ; } if ( ! workingDirectory . exists ( ) ) { getLog ( ) . debug ( "Making working directory '" + workingDirectory . getAbsolutePath ( ) + "'." ) ; if ( ! workingDirectory . mkdirs ( ) ) { throw new MojoExecutionException ( "Could not make working directory: '" + workingDirectory . getAbsolutePath ( ) + "'" ) ; } }


signature:
public class ExecMojo { /** * Compute the classpath from the specified Classpath . The computed classpath is based on the classpathScope . The
 * plugin cannot know from maven the phase it is executed in . So we have to depend on the user to tell us he wants
 * the scope in which the plugin is expected to be executed .
 * @ param specifiedClasspath Non null when the user restricted the dependencies , < code > null < / code > otherwise ( the
 * default classpath will be used )
 * @ return a platform specific String representation of the classpath */
private String computeClasspathString ( AbstractPath specifiedClasspath ) { } }

implementation: List < String > resultList = computePath ( specifiedClasspath ) ; StringBuffer theClasspath = new StringBuffer ( ) ; for ( String str : resultList ) { addToClasspath ( theClasspath , str ) ; } return theClasspath . toString ( ) ;


signature:
public class ExecMojo { /** * Compute the classpath from the specified Classpath . The computed classpath is based on the classpathScope . The
 * plugin cannot know from maven the phase it is executed in . So we have to depend on the user to tell us he wants
 * the scope in which the plugin is expected to be executed .
 * @ param specifiedClasspath Non null when the user restricted the dependencies , < code > null < / code > otherwise ( the
 * default classpath will be used )
 * @ return a list of class path elements */
private List < String > computePath ( AbstractPath specifiedClasspath ) { } }

implementation: List < Artifact > artifacts = new ArrayList < > ( ) ; List < Path > theClasspathFiles = new ArrayList < > ( ) ; List < String > resultList = new ArrayList < > ( ) ; collectProjectArtifactsAndClasspath ( artifacts , theClasspathFiles ) ; if ( ( specifiedClasspath != null ) && ( specifiedClasspath . getDependencies ( ) != null ) ) { artifacts = filterArtifacts ( artifacts , specifiedClasspath . getDependencies ( ) ) ; } for ( Path f : theClasspathFiles ) { resultList . add ( f . toAbsolutePath ( ) . toString ( ) ) ; } for ( Artifact artifact : artifacts ) { getLog ( ) . debug ( "dealing with " + artifact ) ; resultList . add ( artifact . getFile ( ) . getAbsolutePath ( ) ) ; } return resultList ;


signature:
public class ExecMojo { /** * Create a jar with just a manifest containing a Main - Class entry for SurefireBooter and a Class - Path entry for all
 * classpath elements . Copied from surefire ( ForkConfiguration # createJar ( ) )
 * @ param classPath List & lt ; String > of all classpath elements .
 * @ return
 * @ throws IOException */
private File createJar ( List < String > classPath , String mainClass ) throws IOException { } }

implementation:
File file = File . createTempFile ( "maven-exec" , ".jar" ) ; file . deleteOnExit ( ) ; FileOutputStream fos = new FileOutputStream ( file ) ; JarOutputStream jos = new JarOutputStream ( fos ) ; jos . setLevel ( JarOutputStream . STORED ) ; JarEntry je = new JarEntry ( "META-INF/MANIFEST.MF" ) ; jos . putNextEntry ( je ) ; Manifest man = new Manifest ( ) ; // we can ' t use StringUtils . join here since we need to add a ' / ' to
// the end of directory entries - otherwise the jvm will ignore them .
StringBuilder cp = new StringBuilder ( ) ; for ( String el : classPath ) { // NOTE : if File points to a directory , this entry MUST end in ' / ' .
cp . append ( new URL ( new File ( el ) . toURI ( ) . toASCIIString ( ) ) . toExternalForm ( ) + " " ) ; } man . getMainAttributes ( ) . putValue ( "Manifest-Version" , "1.0" ) ; man . getMainAttributes ( ) . putValue ( "Class-Path" , cp . toString ( ) . trim ( ) ) ; man . getMainAttributes ( ) . putValue ( "Main-Class" , mainClass ) ; man . write ( jos ) ; jos . close ( ) ; return file ;


signature:
public class FeedStats { /** * returns null for schedules without calendar service schedules */
public LocalDate getCalendarServiceRangeStart ( ) { } }

implementation:
int startDate = 0 ; for ( Service service : feed . services . values ( ) ) { if ( service . calendar == null ) continue ; // if ( startDate = = 0 | | service . calendar . start _ date < startDate ) {
// startDate = service . calendar . start _ date ;
} if ( startDate == 0 ) return null ; DateTimeFormatter formatter = DateTimeFormatter . ofPattern ( "yyyyMMdd" ) ; return LocalDate . parse ( String . valueOf ( startDate ) , formatter ) ;


signature:
public class FeedStats { /** * in seconds */
public int getDailyAverageHeadway ( LocalDate date , LocalTime from , LocalTime to ) { } }

implementation: OptionalDouble avg = feed . stops . values ( ) . stream ( ) . map ( s -> this . stop . getAverageHeadwayForStop ( s . stop_id , date , from , to ) ) . mapToDouble ( headway -> headway ) . average ( ) ; return ( int ) avg . getAsDouble ( ) ;


signature:
public class FeedStats { /** * Get the bounding box of this GTFS feed .
 * We use a Rectangle2D rather than a Geotools envelope because GTFS is always in WGS 84.
 * Note that stops do not have agencies in GTFS . */
public Rectangle2D getBounds ( ) { } }

implementation:
Rectangle2D ret = null ; for ( Stop stop : feed . stops . values ( ) ) { // skip over stops that don ' t have any stop times
if ( ! feed . stopCountByStopTime . containsKey ( stop . stop_id ) ) { continue ; } if ( ret == null ) { ret = new Rectangle2D . Double ( stop . stop_lon , stop . stop_lat , 0 , 0 ) ; } else { ret . add ( new Point2D . Double ( stop . stop_lon , stop . stop_lat ) ) ; } } return ret ;


signature:
public class SpeedTripValidator { /** * Register shape dist traveled error if current stop time has a value AND either and the previous value is
 * missing ( if at least one stop time has a value , all stop times for the trip should ) OR if current value
 * is less than or equal to the previous value . Note : if the previous shape _ dist _ traveled value is present and the
 * current value is missing , the previous value will be greater than the current stop time ' s value because
 * { @ link Entity # DOUBLE _ MISSING } is the lowest possible double value . This in turn will register an error . */
private void checkShapeDistTraveled ( StopTime previous , StopTime current ) { } }

implementation: if ( current . shape_dist_traveled != Entity . DOUBLE_MISSING && ( previous . shape_dist_traveled == Entity . DOUBLE_MISSING || current . shape_dist_traveled <= previous . shape_dist_traveled ) ) { registerError ( current , SHAPE_DIST_TRAVELED_NOT_INCREASING , current . shape_dist_traveled ) ; }


signature:
public class SpeedTripValidator { /** * Completing this feed validator means checking if there were any unrounded travel times in the feed and ( if so )
 * registering any zero travel time errors that were passed over before the first unrounded travel time was
 * encountered . If in fact all travel times are rounded to the minute , store a special feed - wide error in this case . */
public void complete ( ValidationResult validationResult ) { } }

implementation: if ( ! allTravelTimesAreRounded ) storeErrors ( travelTimeZeroErrors ) ; else registerError ( NewGTFSError . forFeed ( FEED_TRAVEL_TIMES_ROUNDED , null ) ) ;


signature:
public class SpeedTripValidator { /** * Check that arrival and departure time for a stop time are rounded to the minute and update
 * { @ link # allTravelTimesAreRounded } accordingly . */
private boolean areTravelTimesRounded ( StopTime stopTime ) { } }

implementation: boolean bothTravelTimesAreRounded = stopTime . departure_time % 60 == 0 && stopTime . arrival_time % 60 == 0 ; if ( ! bothTravelTimesAreRounded ) this . allTravelTimesAreRounded = false ; return bothTravelTimesAreRounded ;


signature:
public class SpeedTripValidator { /** * This just pulls some of the range checking logic out of the main trip checking loop so it ' s more readable .
 * @ return true if all values are OK */
private boolean checkDistanceAndTime ( double distanceMeters , double travelTimeSeconds , StopTime stopTime ) { } }

implementation:
boolean good = true ; // TODO Use Epsilon for very tiny travel e . g . < 5 meters
if ( distanceMeters == 0 ) { registerError ( stopTime , TRAVEL_DISTANCE_ZERO ) ; good = false ; } if ( travelTimeSeconds < 0 ) { registerError ( stopTime , TRAVEL_TIME_NEGATIVE , travelTimeSeconds ) ; good = false ; } else if ( travelTimeSeconds == 0 ) { // Only register the travel time zero error if not all travel times are rounded . Otherwise , hold onto the
// error in the travelTimeZeroErrors collection until the completion of this validator .
if ( ! allTravelTimesAreRounded ) registerError ( stopTime , TRAVEL_TIME_ZERO ) ; else travelTimeZeroErrors . add ( createUnregisteredError ( stopTime , TRAVEL_TIME_ZERO ) ) ; good = false ; } return good ;


signature:
public class Pattern { /** * Sets the parameters for a prepared statement following the parameter order defined in
 * { @ link com . conveyal . gtfs . loader . Table # PATTERNS } . JDBC prepared statement parameters use a one - based index . */
@ Override public void setStatementParameters ( PreparedStatement statement , boolean setDefaultId ) throws SQLException { } }

implementation:
int oneBasedIndex = 1 ; if ( ! setDefaultId ) statement . setInt ( oneBasedIndex ++ , id ) ; statement . setString ( oneBasedIndex ++ , pattern_id ) ; statement . setString ( oneBasedIndex ++ , route_id ) ; statement . setString ( oneBasedIndex ++ , name ) ; // Editor - specific fields
setIntParameter ( statement , oneBasedIndex ++ , direction_id ) ; // Note : pattern # use _ frequency is set in JdbcGtfsSnapshotter here :
// https : / / github . com / conveyal / gtfs - lib / blob / 0c6aca98a83d534853b74011e6cc7bf376592581 / src / main / java / com / conveyal / gtfs / loader / JdbcGtfsSnapshotter . java # L196 - L211
setIntParameter ( statement , oneBasedIndex ++ , INT_MISSING ) ; // use _ frequency
// FIXME : Shape set might be null ?
statement . setString ( oneBasedIndex ++ , associatedShapes . iterator ( ) . next ( ) ) ;


signature:
public class JdbcGtfsLoader { /** * There appears to be no advantage to loading tables in parallel , as the whole loading process is I / O bound . */
public FeedLoadResult loadTables ( ) { } }

implementation:
// This result object will be returned to the caller to summarize the feed and report any critical errors .
FeedLoadResult result = new FeedLoadResult ( ) ; try { // Begin tracking time . FIXME : should this follow the connect / register and begin with the table loads ?
long startTime = System . currentTimeMillis ( ) ; // We get a single connection object and share it across several different methods .
// This ensures that actions taken in one method are visible to all subsequent SQL statements .
// If we create a schema or table on one connection , then access it in a separate connection , we have no
// guarantee that it exists when the accessing statement is executed .
connection = dataSource . getConnection ( ) ; File gtfsFile = new File ( gtfsFilePath ) ; this . zip = new ZipFile ( gtfsFilePath ) ; // Generate a unique prefix that will identify this feed .
// Prefixes ( " schema " names ) based on feed _ id and feed _ version get very messy , so we use random unique IDs .
// We don ' t want to use an auto - increment numeric primary key because these need to be alphabetical .
// Although ID collisions are theoretically possible , they are improbable in the extreme because our IDs
// are long enough to have as much entropy as a UUID . So we don ' t really need to check for uniqueness and
// retry in a loop .
// TODO handle the case where we don ' t want any prefix .
this . tablePrefix = randomIdString ( ) ; result . filename = gtfsFilePath ; result . uniqueIdentifier = tablePrefix ; // The order of the following four lines should not be changed because the schema needs to be in place
// before the error storage can be constructed , which in turn needs to exist in case any errors are
// encountered during the loading process .
{ createSchema ( connection , tablePrefix ) ; // the SQLErrorStorage constructor expects the tablePrefix to contain the dot separator .
this . errorStorage = new SQLErrorStorage ( connection , tablePrefix + "." , true ) ; // registerFeed accesses this . tablePrefix which shouldn ' t contain the dot separator .
registerFeed ( gtfsFile ) ; // Include the dot separator in the table prefix from this point onwards .
// This allows everything to work even when there ' s no prefix .
this . tablePrefix += "." ; } // Load each table in turn , saving some summary information about what happened during each table load
result . agency = load ( Table . AGENCY ) ; result . calendar = load ( Table . CALENDAR ) ; result . calendarDates = load ( Table . CALENDAR_DATES ) ; result . routes = load ( Table . ROUTES ) ; result . fareAttributes = load ( Table . FARE_ATTRIBUTES ) ; result . fareRules = load ( Table . FARE_RULES ) ; result . feedInfo = load ( Table . FEED_INFO ) ; result . shapes = load ( Table . SHAPES ) ; result . stops = load ( Table . STOPS ) ; result . transfers = load ( Table . TRANSFERS ) ; result . trips = load ( Table . TRIPS ) ; // refs routes
result . frequencies = load ( Table . FREQUENCIES ) ; // refs trips
result . stopTimes = load ( Table . STOP_TIMES ) ; result . errorCount = errorStorage . getErrorCount ( ) ; // This will commit and close the single connection that has been shared between all preceding load steps .
errorStorage . commitAndClose ( ) ; zip . close ( ) ; result . completionTime = System . currentTimeMillis ( ) ; result . loadTimeMillis = result . completionTime - startTime ; LOG . info ( "Loading tables took {} sec" , result . loadTimeMillis / 1000 ) ; } catch ( Exception ex ) { // TODO catch exceptions separately while loading each table so load can continue , store in TableLoadResult
LOG . error ( "Exception while loading GTFS file: {}" , ex . toString ( ) ) ; ex . printStackTrace ( ) ; result . fatalException = ex . toString ( ) ; } return result ;


signature:
public class JdbcGtfsLoader { /** * Creates a schema / namespace in the database WITHOUT committing the changes .
 * This does * not * setup any other tables or enter the schema name in a registry ( @ see # registerFeed ) .
 * @ param connection Connection to the database to create the schema on .
 * @ param schemaName Name of the schema ( i . e . table prefix ) . Should not include the dot suffix . */
static void createSchema ( Connection connection , String schemaName ) { } }

implementation:
try { Statement statement = connection . createStatement ( ) ; // FIXME do the following only on databases that support schemas .
// SQLite does not support them . Is there any advantage of schemas over flat tables ?
statement . execute ( "create schema " + schemaName ) ; LOG . info ( "Created new feed schema: {}" , statement ) ; } catch ( Exception ex ) { LOG . error ( "Exception while registering new feed namespace in feeds table: {}" , ex . getMessage ( ) ) ; DbUtils . closeQuietly ( connection ) ; }


signature:
public class JdbcGtfsLoader { /** * Add a line to the list of loaded feeds showing that this feed has been loaded .
 * We used to inspect feed _ info here so we could make our table prefix based on feed ID and version .
 * Now we just load feed _ info like any other table .
 * / / Create a row in the table of loaded feeds for this feed
 * Really this is not just making the table prefix - it ' s loading the feed _ info and should also calculate hashes .
 * Originally we were flattening all feed _ info files into one root - level table , but that forces us to drop any
 * custom fields in feed _ info . */
private void registerFeed ( File gtfsFile ) { } }

implementation:
// FIXME is this extra CSV reader used anymore ? Check comment below .
// First , inspect feed _ info . txt to extract the ID and version .
// We could get this with SQL after loading , but feed _ info , feed _ id and feed _ version are all optional .
CsvReader csvReader = Table . FEED_INFO . getCsvReader ( zip , errorStorage ) ; String feedId = "" , feedVersion = "" ; if ( csvReader != null ) { // feed _ info . txt has been found and opened .
try { csvReader . readRecord ( ) ; // csvReader . get ( ) returns the empty string for missing columns
feedId = csvReader . get ( "feed_id" ) ; feedVersion = csvReader . get ( "feed_version" ) ; } catch ( IOException e ) { LOG . error ( "Exception while inspecting feed_info: {}" , e ) ; } csvReader . close ( ) ; } try { HashCode md5 = Files . hash ( gtfsFile , Hashing . md5 ( ) ) ; String md5Hex = md5 . toString ( ) ; HashCode sha1 = Files . hash ( gtfsFile , Hashing . sha1 ( ) ) ; String shaHex = sha1 . toString ( ) ; createFeedRegistryIfNotExists ( connection ) ; // TODO try to get the feed _ id and feed _ version out of the feed _ info table
// statement . execute ( " select * from feed _ info " ) ;
// current _ timestamp seems to be the only standard way to get the current time across all common databases .
// Record total load processing time ?
PreparedStatement insertStatement = connection . prepareStatement ( "insert into feeds values (?, ?, ?, ?, ?, ?, current_timestamp, null)" ) ; insertStatement . setString ( 1 , tablePrefix ) ; insertStatement . setString ( 2 , md5Hex ) ; insertStatement . setString ( 3 , shaHex ) ; insertStatement . setString ( 4 , feedId . isEmpty ( ) ? null : feedId ) ; insertStatement . setString ( 5 , feedVersion . isEmpty ( ) ? null : feedVersion ) ; insertStatement . setString ( 6 , zip . getName ( ) ) ; insertStatement . execute ( ) ; connection . commit ( ) ; LOG . info ( "Created new feed namespace: {}" , insertStatement ) ; } catch ( Exception ex ) { LOG . error ( "Exception while registering new feed namespace in feeds table" , ex ) ; DbUtils . closeQuietly ( connection ) ; }


signature:
public class JdbcGtfsLoader { /** * Creates the feed registry table if it does not already exist . This must occur before the first attempt to load a
 * GTFS feed or create an empty snapshot . Note : the connection MUST be committed after this method call . */
static void createFeedRegistryIfNotExists ( Connection connection ) throws SQLException { } }

implementation: Statement statement = connection . createStatement ( ) ; statement . execute ( "create table if not exists feeds (namespace varchar primary key, md5 varchar, " + "sha1 varchar, feed_id varchar, feed_version varchar, filename varchar, loaded_date timestamp, " + "snapshot_of varchar)" ) ;


signature:
public class JdbcGtfsLoader { /** * This wraps the main internal table loader method to catch exceptions and figure out how many errors happened . */
private TableLoadResult load ( Table table ) { } }

implementation:
// This object will be returned to the caller to summarize the contents of the table and any errors .
TableLoadResult tableLoadResult = new TableLoadResult ( ) ; int initialErrorCount = errorStorage . getErrorCount ( ) ; try { tableLoadResult . rowCount = loadInternal ( table ) ; tableLoadResult . fileSize = getTableSize ( table ) ; LOG . info ( String . format ( "loaded in %d %s records" , tableLoadResult . rowCount , table . name ) ) ; } catch ( Exception ex ) { LOG . error ( "Fatal error loading table" , ex ) ; tableLoadResult . fatalException = ex . toString ( ) ; // Rollback connection so that fatal exception does not impact loading of other tables .
try { connection . rollback ( ) ; } catch ( SQLException e ) { e . printStackTrace ( ) ; } } finally { // Explicitly delete the tmp file now that load is finished ( either success or failure ) .
// Otherwise these multi - GB files clutter the drive .
if ( tempTextFile != null ) { tempTextFile . delete ( ) ; } } int finalErrorCount = errorStorage . getErrorCount ( ) ; tableLoadResult . errorCount = finalErrorCount - initialErrorCount ; return tableLoadResult ;


signature:
public class JdbcGtfsLoader { /** * Get the uncompressed file size in bytes for the specified GTFS table . */
private int getTableSize ( Table table ) { } }

implementation: ZipEntry zipEntry = zip . getEntry ( table . name + ".txt" ) ; if ( zipEntry == null ) return 0 ; return ( int ) zipEntry . getSize ( ) ;


signature:
public class JdbcGtfsLoader { /** * This function will throw any exception that occurs . Those exceptions will be handled by the outer load method .
 * @ return number of rows that were loaded . */
private int loadInternal ( Table table ) throws Exception { } }

implementation:
CsvReader csvReader = table . getCsvReader ( zip , errorStorage ) ; if ( csvReader == null ) { LOG . info ( String . format ( "file %s.txt not found in gtfs zipfile" , table . name ) ) ; // This GTFS table could not be opened in the zip , even in a subdirectory .
if ( table . isRequired ( ) ) errorStorage . storeError ( NewGTFSError . forTable ( table , MISSING_TABLE ) ) ; return 0 ; } LOG . info ( "Loading GTFS table {}" , table . name ) ; // Use the Postgres text load format if we ' re connected to that DBMS .
boolean postgresText = ( connection . getMetaData ( ) . getDatabaseProductName ( ) . equals ( "PostgreSQL" ) ) ; // TODO Strip out line returns , tabs in field contents .
// By default the CSV reader trims leading and trailing whitespace in fields .
// Build up a list of fields in the same order they appear in this GTFS CSV file .
Field [ ] fields = table . getFieldsFromFieldHeaders ( csvReader . getHeaders ( ) , errorStorage ) ; int keyFieldIndex = table . getKeyFieldIndex ( fields ) ; // Create separate fields array with filtered list that does not include null values ( for duplicate headers or
// ID field ) . This is solely used to construct the table and array of values to load .
Field [ ] cleanFields = Arrays . stream ( fields ) . filter ( field -> field != null ) . toArray ( Field [ ] :: new ) ; if ( cleanFields . length == 0 ) { // Do not create the table if there are no valid fields .
errorStorage . storeError ( NewGTFSError . forTable ( table , TABLE_MISSING_COLUMN_HEADERS ) ) ; return 0 ; } // Replace the GTFS spec Table with one representing the SQL table we will populate , with reordered columns .
// FIXME this is confusing , we only create a new table object so we can call a couple of methods on it , all of which just need a list of fields .
Table targetTable = new Table ( tablePrefix + table . name , table . entityClass , table . required , cleanFields ) ; // NOTE H2 doesn ' t seem to work with schemas ( or create schema doesn ' t work ) .
// With bulk loads it takes 140 seconds to load the data and additional 120 seconds just to index the stop times .
// SQLite also doesn ' t support schemas , but you can attach additional database files with schema - like naming .
// We ' ll just literally prepend feed identifiers to table names when supplied .
// Some databases require the table to exist before a statement can be prepared .
targetTable . createSqlTable ( connection ) ; // TODO are we loading with or without a header row in our Postgres text file ?
if ( postgresText ) { // No need to output headers to temp text file , our SQL table column order exactly matches our text file .
tempTextFile = File . createTempFile ( targetTable . name , "text" ) ; tempTextFileStream = new PrintStream ( new BufferedOutputStream ( new FileOutputStream ( tempTextFile ) ) ) ; LOG . info ( "Loading via temporary text file at " + tempTextFile . getAbsolutePath ( ) ) ; } else { insertStatement = connection . prepareStatement ( targetTable . generateInsertSql ( ) ) ; LOG . info ( insertStatement . toString ( ) ) ; // Logs the SQL for the prepared statement
} // When outputting text , accumulate transformed strings to allow skipping rows when errors are encountered .
// One extra position in the array for the CSV line number .
String [ ] transformedStrings = new String [ cleanFields . length + 1 ] ; // Iterate over each record and prepare the record for storage in the table either through batch insert
// statements or postgres text copy operation .
while ( csvReader . readRecord ( ) ) { // The CSV reader ' s current record is zero - based and does not include the header line .
// Convert to a CSV file line number that will make more sense to people reading error messages .
if ( csvReader . getCurrentRecord ( ) + 2 > Integer . MAX_VALUE ) { errorStorage . storeError ( NewGTFSError . forTable ( table , TABLE_TOO_LONG ) ) ; break ; } int lineNumber = ( ( int ) csvReader . getCurrentRecord ( ) ) + 2 ; if ( lineNumber % 500_000 == 0 ) LOG . info ( "Processed {}" , human ( lineNumber ) ) ; if ( csvReader . getColumnCount ( ) != fields . length ) { String badValues = String . format ( "expected=%d; found=%d" , fields . length , csvReader . getColumnCount ( ) ) ; errorStorage . storeError ( NewGTFSError . forLine ( table , lineNumber , WRONG_NUMBER_OF_FIELDS , badValues ) ) ; continue ; } // Store value of key field for use in checking duplicate IDs
// FIXME : If the key field is missing ( keyFieldIndex is still - 1 ) from a loaded table , this will crash .
String keyValue = csvReader . get ( keyFieldIndex ) ; // The first field holds the line number of the CSV file . Prepared statement parameters are one - based .
if ( postgresText ) transformedStrings [ 0 ] = Integer . toString ( lineNumber ) ; else insertStatement . setInt ( 1 , lineNumber ) ; // Maintain a separate columnIndex from for loop because some fields may be null and not included in the set
// of fields for this table .
int columnIndex = 0 ; for ( int f = 0 ; f < fields . length ; f ++ ) { Field field = fields [ f ] ; // If the field is null , it represents a duplicate header or ID field and must be skipped to maintain
// table integrity .
if ( field == null ) continue ; // CSV reader get on an empty field will be an empty string literal .
String string = csvReader . get ( f ) ; // Use spec table to check that references are valid and IDs are unique .
Set < NewGTFSError > errors = referenceTracker . checkReferencesAndUniqueness ( keyValue , lineNumber , field , string , table ) ; // Check for special case with calendar _ dates where added service should not trigger ref . integrity
// error .
if ( table . name . equals ( "calendar_dates" ) && "service_id" . equals ( field . name ) && "1" . equals ( csvReader . get ( Field . getFieldIndex ( fields , "exception_type" ) ) ) ) { for ( NewGTFSError error : errors ) { if ( NewGTFSErrorType . REFERENTIAL_INTEGRITY . equals ( error . errorType ) ) { // Do not record bad service _ id reference errors for calendar date entries that add service
// ( exception type = 1 ) because a corresponding service _ id in calendars . txt is not required in
// this case .
LOG . info ( "A calendar_dates.txt entry added service (exception_type=1) for service_id={}, which does not have (or necessarily need) a corresponding entry in calendars.txt." , keyValue ) ; } else { errorStorage . storeError ( error ) ; } } } // In all other cases ( i . e . , outside of the calendar _ dates special case ) , store the reference errors found .
else { errorStorage . storeErrors ( errors ) ; } // Add value for entry into table
setValueForField ( table , columnIndex , lineNumber , field , string , postgresText , transformedStrings ) ; // Increment column index .
columnIndex += 1 ; } if ( postgresText ) { // Print a new line in the standard postgres text format :
// https : / / www . postgresql . org / docs / 9.1 / static / sql - copy . html # AEN64380
tempTextFileStream . println ( String . join ( "\t" , transformedStrings ) ) ; } else { insertStatement . addBatch ( ) ; if ( lineNumber % INSERT_BATCH_SIZE == 0 ) insertStatement . executeBatch ( ) ; } } // Record number is zero based but includes the header record , which we don ' t want to count .
// But if we are working with Postgres text file ( without a header row ) we have to add 1
// Iteration over all rows has finished , so We are now one record past the end of the file .
int numberOfRecordsLoaded = ( int ) csvReader . getCurrentRecord ( ) ; if ( postgresText ) { numberOfRecordsLoaded = numberOfRecordsLoaded + 1 ; } if ( table . isRequired ( ) && numberOfRecordsLoaded == 0 ) { errorStorage . storeError ( NewGTFSError . forTable ( table , REQUIRED_TABLE_EMPTY ) ) ; } csvReader . close ( ) ; // Finalize loading the table , either by copying the pre - validated text file into the database ( for Postgres )
// or inserting any remaining rows ( for all others ) .
if ( postgresText ) { LOG . info ( "Loading into database table {} from temporary text file..." , targetTable . name ) ; tempTextFileStream . close ( ) ; copyFromFile ( connection , tempTextFile , targetTable . name ) ; } else { insertStatement . executeBatch ( ) ; } // Create indexes using spec table . Target table must not be used because fields could be in the wrong order
// ( and the order is currently important to determining the index fields ) .
table . createIndexes ( connection , tablePrefix ) ; LOG . info ( "Committing transaction..." ) ; connection . commit ( ) ; LOG . info ( "Done." ) ; return numberOfRecordsLoaded ;


signature:
public class JdbcGtfsLoader { /** * Method that uses the PostgreSQL - specific copy from file command to load csv data into a table on the provided
 * connection . NOTE : This method does not commit the transaction or close the connection . */
public static void copyFromFile ( Connection connection , File file , String targetTableName ) throws IOException , SQLException { } }

implementation:
// Allows sending over network . This is only slightly slower than a local file copy .
final String copySql = String . format ( "copy %s from stdin" , targetTableName ) ; // FIXME we should be reading the COPY text from a stream in parallel , not from a temporary text file .
InputStream stream = new BufferedInputStream ( new FileInputStream ( file . getAbsolutePath ( ) ) ) ; // Our connection pool wraps the Connection objects , so we need to unwrap the Postgres connection interface .
CopyManager copyManager = new CopyManager ( connection . unwrap ( BaseConnection . class ) ) ; copyManager . copyIn ( copySql , stream , 1024 * 1024 ) ; stream . close ( ) ; // It is also possible to load from local file if this code is running on the database server .
// statement . execute ( String . format ( " copy % s from ' % s ' " , table . name , tempTextFile . getAbsolutePath ( ) ) ) ;


signature:
public class JdbcGtfsLoader { /** * Set value for a field either as a prepared statement parameter or ( if using postgres text - loading ) in the
 * transformed strings array provided . This also handles the case where the string is empty ( i . e . , field is null )
 * and when an exception is encountered while setting the field value ( usually due to a bad data type ) , in which case
 * the field is set to null . */
public void setValueForField ( Table table , int fieldIndex , int lineNumber , Field field , String string , boolean postgresText , String [ ] transformedStrings ) { } }

implementation:
if ( string . isEmpty ( ) ) { // CSV reader always returns empty strings , not nulls
if ( field . isRequired ( ) && ! field . isEmptyValuePermitted ( ) && errorStorage != null ) { errorStorage . storeError ( NewGTFSError . forLine ( table , lineNumber , MISSING_FIELD , field . name ) ) ; } setFieldToNull ( postgresText , transformedStrings , fieldIndex , field ) ; } else { // Micro - benchmarks show it ' s only 4-5 % faster to call typed parameter setter methods
// rather than setObject with a type code . I think some databases don ' t have setObject though .
// The Field objects throw exceptions to avoid passing the line number , table name etc . into them .
try { // Here , we set the transformed string element even when an error occurs .
// Ideally , no errors should be signaled with exceptions , but this happens in a try / catch in case
// something goes wrong ( we don ' t necessarily want to abort loading the feed altogether ) .
// FIXME Also , we should probably not be converting any GTFS field values , but some of them are coerced
// to null if they are unparseable ( e . g . , DateField ) .
// We should be saving it as - is in the database and converting upon load into our model objects .
Set < NewGTFSError > errors ; if ( postgresText ) { ValidateFieldResult < String > result = field . validateAndConvert ( string ) ; // If the result is null , use the null - setting method .
if ( result . clean == null ) setFieldToNull ( postgresText , transformedStrings , fieldIndex , field ) ; // Otherwise , set the cleaned field according to its index .
else transformedStrings [ fieldIndex + 1 ] = result . clean ; errors = result . errors ; } else { errors = field . setParameter ( insertStatement , fieldIndex + 2 , string ) ; } // Store any errors encountered after field value has been set .
for ( NewGTFSError error : errors ) { error . entityType = table . getEntityClass ( ) ; error . lineNumber = lineNumber ; if ( errorStorage != null ) errorStorage . storeError ( error ) ; } } catch ( StorageException ex ) { // FIXME many exceptions don ' t have an error type
if ( errorStorage != null ) { errorStorage . storeError ( NewGTFSError . forLine ( table , lineNumber , ex . errorType , ex . badValue ) ) ; } // Set transformedStrings or prepared statement param to null
setFieldToNull ( postgresText , transformedStrings , fieldIndex , field ) ; } }


signature:
public class JdbcGtfsLoader { /** * Sets field to null in statement or string array depending on whether postgres is being used . */
private void setFieldToNull ( boolean postgresText , String [ ] transformedStrings , int fieldIndex , Field field ) { } }

implementation:
if ( postgresText ) transformedStrings [ fieldIndex + 1 ] = POSTGRES_NULL_TEXT ; // Adjust parameter index by two : indexes are one - based and the first one is the CSV line number .
else try { // LOG . info ( " setting { } index to null " , fieldIndex + 2 ) ;
field . setNull ( insertStatement , fieldIndex + 2 ) ; } catch ( SQLException e ) { e . printStackTrace ( ) ; // FIXME : store error here ? It appears that an exception should only be thrown if the type value is invalid ,
// the connection is closed , or the index is out of bounds . So storing an error may be unnecessary .
}


signature:
public class JdbcGtfsLoader { /** * Protect against SQL injection .
 * The only place we include arbitrary input in SQL is the column names of tables .
 * Implicitly ( looking at all existing table names ) these should consist entirely of
 * lowercase letters and underscores .
 * TODO add a test including SQL injection text ( quote and semicolon ) */
public static String sanitize ( String string , SQLErrorStorage errorStorage ) { } }

implementation: String clean = string . replaceAll ( "[^\\p{Alnum}_]" , "" ) ; if ( ! clean . equals ( string ) ) { LOG . warn ( "SQL identifier '{}' was sanitized to '{}'" , string , clean ) ; if ( errorStorage != null ) errorStorage . storeError ( NewGTFSError . forFeed ( COLUMN_NAME_UNSAFE , string ) ) ; } return clean ;


signature:
public class FareRule { /** * Sets the parameters for a prepared statement following the parameter order defined in
 * { @ link com . conveyal . gtfs . loader . Table # FARE _ RULES } . JDBC prepared statement parameters use a one - based index . */
@ Override public void setStatementParameters ( PreparedStatement statement , boolean setDefaultId ) throws SQLException { } }

implementation: int oneBasedIndex = 1 ; if ( ! setDefaultId ) statement . setInt ( oneBasedIndex ++ , id ) ; statement . setString ( oneBasedIndex ++ , fare_id ) ; statement . setString ( oneBasedIndex ++ , route_id ) ; statement . setString ( oneBasedIndex ++ , origin_id ) ; statement . setString ( oneBasedIndex ++ , destination_id ) ; statement . setString ( oneBasedIndex ++ , contains_id ) ;