signature:
public class JDBCFetcher { /** * Construct filter clause with ' = ' ( single string ) or ' in ' ( multiple strings ) and add values to
 * list of parameters . */
static String makeInClause ( String filterField , List < String > strings , List < String > parameters ) { } }

implementation:
if ( strings . size ( ) == 1 ) { return filterEquals ( filterField , strings . get ( 0 ) , parameters ) ; } else { // Add strings to list of parameters ( to be later used to set parameters for prepared statement ) .
parameters . addAll ( strings ) ; String questionMarks = String . join ( ", " , Collections . nCopies ( strings . size ( ) , "?" ) ) ; return String . format ( "%s in (%s)" , filterField , questionMarks ) ; }


signature:
public class GTFSGraphQL { /** * Username and password can be null if connecting to a local instance with host - based authentication . */
public static void initialize ( DataSource dataSource ) { } }

implementation: GTFSGraphQL . dataSource = dataSource ; GRAPHQL = GraphQL . newGraphQL ( GraphQLGtfsSchema . feedBasedSchema ) . build ( ) ;


signature:
public class Table { /** * Get only those fields included in the official GTFS specification for this table or used by the editor . */
public List < Field > editorFields ( ) { } }

implementation: List < Field > editorFields = new ArrayList < > ( ) ; for ( Field f : fields ) if ( f . requirement == REQUIRED || f . requirement == OPTIONAL || f . requirement == EDITOR ) { editorFields . add ( f ) ; } return editorFields ;


signature:
public class Table { /** * Get only those fields marked as required in the official GTFS specification for this table . */
public List < Field > requiredFields ( ) { } }

implementation:
// Filter out fields not used in editor ( i . e . , extension fields ) .
List < Field > requiredFields = new ArrayList < > ( ) ; for ( Field f : fields ) if ( f . requirement == REQUIRED ) requiredFields . add ( f ) ; return requiredFields ;


signature:
public class Table { /** * Get only those fields included in the official GTFS specification for this table , i . e . , filter out fields used
 * in the editor or extensions . */
public List < Field > specFields ( ) { } }

implementation: List < Field > specFields = new ArrayList < > ( ) ; for ( Field f : fields ) if ( f . requirement == REQUIRED || f . requirement == OPTIONAL ) specFields . add ( f ) ; return specFields ;


signature:
public class Table { /** * Create an SQL table with all the fields specified by this table object ,
 * plus an integer CSV line number field in the first position . */
public boolean createSqlTable ( Connection connection , String namespace , boolean makeIdSerial , String [ ] primaryKeyFields ) { } }

implementation:
// Optionally join namespace and name to create full table name if namespace is not null ( i . e . , table object is
// a spec table ) .
String tableName = namespace != null ? String . join ( "." , namespace , name ) : name ; String fieldDeclarations = Arrays . stream ( fields ) . map ( Field :: getSqlDeclaration ) . collect ( Collectors . joining ( ", " ) ) ; if ( primaryKeyFields != null ) { fieldDeclarations += String . format ( ", primary key (%s)" , String . join ( ", " , primaryKeyFields ) ) ; } String dropSql = String . format ( "drop table if exists %s" , tableName ) ; // Adding the unlogged keyword gives about 12 percent speedup on loading , but is non - standard .
String idFieldType = makeIdSerial ? "serial" : "bigint" ; String createSql = String . format ( "create table %s (id %s not null, %s)" , tableName , idFieldType , fieldDeclarations ) ; try { Statement statement = connection . createStatement ( ) ; LOG . info ( dropSql ) ; statement . execute ( dropSql ) ; LOG . info ( createSql ) ; return statement . execute ( createSql ) ; } catch ( Exception ex ) { throw new StorageException ( ex ) ; }


signature:
public class Table { /** * Create SQL string for use in insert statement . Note , this filters table ' s fields to only those used in editor . */
public String generateInsertSql ( String namespace , boolean setDefaultId ) { } }

implementation: String tableName = namespace == null ? name : String . join ( "." , namespace , name ) ; String joinedFieldNames = commaSeparatedNames ( editorFields ( ) ) ; String idValue = setDefaultId ? "DEFAULT" : "?" ; return String . format ( "insert into %s (id, %s) values (%s, %s)" , tableName , joinedFieldNames , idValue , String . join ( ", " , Collections . nCopies ( editorFields ( ) . size ( ) , "?" ) ) ) ;


signature:
public class Table { /** * In GTFS feeds , all files are supposed to be in the root of the zip file , but feed producers often put them
 * in a subdirectory . This function will search subdirectories if the entry is not found in the root .
 * It records an error if the entry is in a subdirectory ( as long as errorStorage is not null ) .
 * It then creates a CSV reader for that table if it ' s found . */
public CsvReader getCsvReader ( ZipFile zipFile , SQLErrorStorage sqlErrorStorage ) { } }

implementation:
final String tableFileName = this . name + ".txt" ; ZipEntry entry = zipFile . getEntry ( tableFileName ) ; if ( entry == null ) { // Table was not found , check if it is in a subdirectory .
Enumeration < ? extends ZipEntry > entries = zipFile . entries ( ) ; while ( entries . hasMoreElements ( ) ) { ZipEntry e = entries . nextElement ( ) ; if ( e . getName ( ) . endsWith ( tableFileName ) ) { entry = e ; if ( sqlErrorStorage != null ) sqlErrorStorage . storeError ( NewGTFSError . forTable ( this , TABLE_IN_SUBDIRECTORY ) ) ; break ; } } } if ( entry == null ) return null ; try { InputStream zipInputStream = zipFile . getInputStream ( entry ) ; // Skip any byte order mark that may be present . Files must be UTF - 8,
// but the GTFS spec says that " files that include the UTF byte order mark are acceptable " .
InputStream bomInputStream = new BOMInputStream ( zipInputStream ) ; CsvReader csvReader = new CsvReader ( bomInputStream , ',' , Charset . forName ( "UTF8" ) ) ; csvReader . readHeaders ( ) ; return csvReader ; } catch ( IOException e ) { LOG . error ( "Exception while opening zip entry: {}" , e ) ; e . printStackTrace ( ) ; return null ; }


signature:
public class Table { /** * Prepend a prefix string to each field and join them with a comma + space separator .
 * Also , if an export to GTFS is being performed , certain fields need a translation from the database format to the
 * GTFS format . Otherwise , the fields are assumed to be asked in order to do a database - to - database export and so
 * the verbatim values of the fields are needed . */
public static String commaSeparatedNames ( List < Field > fieldsToJoin , String prefix , boolean csvOutput ) { } }

implementation:
return fieldsToJoin . stream ( ) // NOTE : This previously only prefixed fields that were foreign refs or key fields . However , this
// caused an issue where shared fields were ambiguously referenced in a select query ( specifically ,
// wheelchair _ accessible in routes and trips ) . So this filter has been removed .
. map ( f -> f . getColumnExpression ( prefix , csvOutput ) ) . collect ( Collectors . joining ( ", " ) ) ;


signature:
public class Table { /** * Create SQL string for use in update statement . Note , this filters table ' s fields to only those used in editor . */
public String generateUpdateSql ( String namespace , int id ) { } }

implementation:
// Collect field names for string joining from JsonObject .
String joinedFieldNames = editorFields ( ) . stream ( ) // If updating , add suffix for use in set clause
. map ( field -> field . name + " = ?" ) . collect ( Collectors . joining ( ", " ) ) ; String tableName = namespace == null ? name : String . join ( "." , namespace , name ) ; return String . format ( "update %s set %s where id = %d" , tableName , joinedFieldNames , id ) ;


signature:
public class Table { /** * Generate select all SQL string . The minimum requirement parameter is used to determine which fields ought to be
 * included in the select statement . For example , if " OPTIONAL " is passed in , both optional and required fields
 * are included in the select . If " EDITOR " is the minimum requirement , editor , optional , and required fields will
 * all be included . */
public String generateSelectSql ( String namespace , Requirement minimumRequirement ) { } }

implementation: String fieldsString ; String tableName = String . join ( "." , namespace , name ) ; String fieldPrefix = tableName + "." ; if ( minimumRequirement . equals ( EDITOR ) ) { fieldsString = commaSeparatedNames ( editorFields ( ) , fieldPrefix , true ) ; } else if ( minimumRequirement . equals ( OPTIONAL ) ) { fieldsString = commaSeparatedNames ( specFields ( ) , fieldPrefix , true ) ; } else if ( minimumRequirement . equals ( REQUIRED ) ) { fieldsString = commaSeparatedNames ( requiredFields ( ) , fieldPrefix , true ) ; } else fieldsString = "*" ; return String . format ( "select %s from %s" , fieldsString , tableName ) ;


signature:
public class Table { /** * Generate a select statement from the columns that actually exist in the database table . This method is intended
 * to be used when exporting to a GTFS and eventually generates the select all with each individual field and
 * applicable transformations listed out . */
public String generateSelectAllExistingFieldsSql ( Connection connection , String namespace ) throws SQLException { } }

implementation:
// select all columns from table
// FIXME This is postgres - specific and needs to be made generic for non - postgres databases .
PreparedStatement statement = connection . prepareStatement ( "SELECT column_name FROM information_schema.columns WHERE table_schema = ? AND table_name = ?" ) ; statement . setString ( 1 , namespace ) ; statement . setString ( 2 , name ) ; ResultSet result = statement . executeQuery ( ) ; // get result and add fields that are defined in this table
List < Field > existingFields = new ArrayList < > ( ) ; while ( result . next ( ) ) { String columnName = result . getString ( 1 ) ; existingFields . add ( getFieldForName ( columnName ) ) ; } String tableName = String . join ( "." , namespace , name ) ; String fieldPrefix = tableName + "." ; return String . format ( "select %s from %s" , commaSeparatedNames ( existingFields , fieldPrefix , true ) , tableName ) ;


signature:
public class Table { /** * Constructs a join clause to use in conjunction with { @ link # generateJoinSql } . By default the join type is " INNER
 * JOIN " and the join field is whatever the table instance ' s key field is . Both of those defaults can be overridden
 * with the other overloaded methods .
 * @ param optionalSelect optional select query to pre - select the join table
 * @ param joinTable the Table to join with
 * @ param joinType type of join ( e . g . , INNER JOIN , OUTER LEFT JOIN , etc . )
 * @ param namespace feedId ( or schema prefix )
 * @ param fieldName the field to join on ( default ' s to key field )
 * @ param prefixTableName whether to prefix this table ' s name with the schema ( helpful if this join follows a
 * previous join that renamed the table by dropping the schema / namespace
 * @ return a fully formed join clause that can be appended to a select statement */
public String generateJoinSql ( String optionalSelect , Table joinTable , String joinType , String namespace , String fieldName , boolean prefixTableName ) { } }

implementation:
if ( fieldName == null ) { // Default to key field if field name is not specified
fieldName = getKeyFieldName ( ) ; } if ( joinType == null ) { // Default to INNER JOIN if no join type provided
joinType = "INNER JOIN" ; } String joinTableQuery ; String joinTableName ; if ( optionalSelect != null ) { // If a pre - select query is provided for the join table , the join table name must not contain the namespace
// prefix .
joinTableName = joinTable . name ; joinTableQuery = String . format ( "(%s) %s" , optionalSelect , joinTableName ) ; } else { // Otherwise , set both the join " query " and the table name to the standard " namespace . table _ name "
joinTableQuery = joinTableName = String . format ( "%s.%s" , namespace , joinTable . name ) ; } // If prefix table name is set to false , skip prefixing the table name .
String tableName = prefixTableName ? String . format ( "%s.%s" , namespace , this . name ) : this . name ; // Construct the join clause . A sample might appear like :
// " INNER JOIN schema . join _ table ON schema . this _ table . field _ name = schema . join _ table . field _ name "
// OR if optionalSelect is specified
// " INNER JOIN ( select * from schema . join _ table where x = ' y ' ) join _ table ON schema . this _ table . field _ name = join _ table . field _ name "
return String . format ( "%s %s ON %s.%s = %s.%s" , joinType , joinTableQuery , tableName , fieldName , joinTableName , fieldName ) ;


signature:
public class Table { /** * Generate delete SQL string . */
public String generateDeleteSql ( String namespace , String fieldName ) { } }

implementation: String whereField = fieldName == null ? "id" : fieldName ; return String . format ( "delete from %s where %s = ?" , String . join ( "." , namespace , name ) , whereField ) ;


signature:
public class Table { /** * Returns field name that defines order for grouped entities or that defines the compound key field ( e . g . ,
 * transfers # to _ stop _ id ) . WARNING : this field must be in the 1st position ( base zero ) of the fields array ; hence ,
 * this MUST be called on a spec table ( i . e . , one of the constant tables defined in this class ) . Otherwise , it could
 * return null even if the table has an order field defined . */
public String getOrderFieldName ( ) { } }

implementation: String name = fields [ 1 ] . name ; if ( name . contains ( "_sequence" ) || compoundKey ) return name ; else return null ;


signature:
public class Table { /** * Gets index fields for the spec table . WARNING : this MUST be called on a spec table ( i . e . , one of the constant
 * tables defined in this class ) . Otherwise , it could return fields that should not be indexed .
 * @ return */
public String getIndexFields ( ) { } }

implementation: String orderFieldName = getOrderFieldName ( ) ; if ( orderFieldName == null ) return getKeyFieldName ( ) ; else return String . join ( "," , getKeyFieldName ( ) , orderFieldName ) ;


signature:
public class Table { /** * Create indexes for table using shouldBeIndexed ( ) , key field , and / or sequence field . WARNING : this MUST be called
 * on a spec table ( i . e . , one of the constant tables defined in this class ) . Otherwise , the getIndexFields method
 * could return fields that should not be indexed .
 * FIXME : add foreign reference indexes ? */
public void createIndexes ( Connection connection , String namespace ) throws SQLException { } }

implementation:
if ( "agency" . equals ( name ) || "feed_info" . equals ( name ) ) { // Skip indexing for the small tables that have so few records that indexes are unlikely to
// improve query performance or that are unlikely to be joined to other tables . NOTE : other tables could be
// added here in the future as needed .
LOG . info ( "Skipping indexes for {} table" , name ) ; return ; } LOG . info ( "Indexing {}..." , name ) ; String tableName ; if ( namespace == null ) { throw new IllegalStateException ( "Schema namespace must be provided!" ) ; } else { // Construct table name with prefixed namespace ( and account for whether it already ends in a period ) .
tableName = namespace . endsWith ( "." ) ? namespace + name : String . join ( "." , namespace , name ) ; } // We determine which columns should be indexed based on field order in the GTFS spec model table .
// Not sure that ' s a good idea , this could use some abstraction . TODO getIndexColumns ( ) on each table .
String indexColumns = getIndexFields ( ) ; // TODO verify referential integrity and uniqueness of keys
// TODO create primary key and fall back on plain index ( consider not null & unique constraints )
// TODO use line number as primary key
// Note : SQLITE requires specifying a name for indexes .
String indexName = String . join ( "_" , tableName . replace ( "." , "_" ) , "idx" ) ; String indexSql = String . format ( "create index %s on %s (%s)" , indexName , tableName , indexColumns ) ; // String indexSql = String . format ( " alter table % s add primary key ( % s ) " , tableName , indexColumns ) ;
LOG . info ( indexSql ) ; connection . createStatement ( ) . execute ( indexSql ) ; // TODO add foreign key constraints , and recover recording errors as needed .
// More indexing
// TODO integrate with the above indexing code , iterating over a List < String > of index column expressions
for ( Field field : fields ) { if ( field . shouldBeIndexed ( ) ) { Statement statement = connection . createStatement ( ) ; String fieldIndex = String . join ( "_" , tableName . replace ( "." , "_" ) , field . name , "idx" ) ; String sql = String . format ( "create index %s on %s (%s)" , fieldIndex , tableName , field . name ) ; LOG . info ( sql ) ; statement . execute ( sql ) ; } }


signature:
public class Table { /** * Creates a SQL table from the table to clone . This uses the SQL syntax " create table x as y " not only copies the
 * table structure , but also the data from the original table . Creating table indexes is not handled by this method . */
public boolean createSqlTableFrom ( Connection connection , String tableToClone ) { } }

implementation:
long startTime = System . currentTimeMillis ( ) ; try { Statement statement = connection . createStatement ( ) ; // Drop target table to avoid a conflict .
String dropSql = String . format ( "drop table if exists %s" , name ) ; LOG . info ( dropSql ) ; statement . execute ( dropSql ) ; if ( tableToClone . endsWith ( "stop_times" ) ) { normalizeAndCloneStopTimes ( statement , name , tableToClone ) ; } else { // Adding the unlogged keyword gives about 12 percent speedup on loading , but is non - standard .
// FIXME : Which create table operation is more efficient ?
String createTableAsSql = String . format ( "create table %s as table %s" , name , tableToClone ) ; // Create table in the image of the table we ' re copying ( indexes are not included ) .
LOG . info ( createTableAsSql ) ; statement . execute ( createTableAsSql ) ; } applyAutoIncrementingSequence ( statement ) ; // FIXME : Is there a need to add primary key constraint here ?
if ( usePrimaryKey ) { // Add primary key to ID column for any tables that require it .
String addPrimaryKeySql = String . format ( "ALTER TABLE %s ADD PRIMARY KEY (id)" , name ) ; LOG . info ( addPrimaryKeySql ) ; statement . execute ( addPrimaryKeySql ) ; } return true ; } catch ( SQLException ex ) { LOG . error ( "Error cloning table {}: {}" , name , ex . getSQLState ( ) ) ; LOG . error ( "details: " , ex ) ; try { connection . rollback ( ) ; // It is likely that if cloning the table fails , the reason was that the table did not already exist .
// Try to create the table here from scratch .
// FIXME : Maybe we should check that the reason the clone failed was that the table already exists .
createSqlTable ( connection , true ) ; return true ; } catch ( SQLException e ) { e . printStackTrace ( ) ; return false ; } } finally { LOG . info ( "Cloned table {} as {} in {} ms" , tableToClone , name , System . currentTimeMillis ( ) - startTime ) ; }


signature:
public class Table { /** * Normalize stop sequences for stop times table so that sequences are all zero - based and increment
 * by one . This ensures that sequence values for stop _ times and pattern _ stops are not initially out
 * of sync for feeds imported into the editor .
 * NOTE : This happens here instead of as part of post - processing because it ' s much faster overall to perform
 * this as an INSERT vs . an UPDATE . It also needs to be done before creating table indexes . There may be some
 * messiness here as far as using the column metadata to perform the SELECT query with the correct column names , but
 * it is about an order of magnitude faster than the UPDATE approach .
 * For example , with the Bronx bus feed , the UPDATE approach took 53 seconds on an un - normalized table ( i . e . , snapshotting
 * a table from a direct GTFS load ) , but it only takes about 8 seconds with the INSERT approach . Additionally , this
 * INSERT approach seems to dramatically cut down on the time needed for indexing large tables . */
private void normalizeAndCloneStopTimes ( Statement statement , String name , String tableToClone ) throws SQLException { } }

implementation:
// Create table with matching columns first and then insert all rows with a special select query that
// normalizes the stop sequences before inserting .
// " Create table like " can optionally include indexes , but we want to avoid creating the indexes beforehand
// because this will slow down our massive insert for stop times .
String createTableLikeSql = String . format ( "create table %s (like %s)" , name , tableToClone ) ; LOG . info ( createTableLikeSql ) ; statement . execute ( createTableLikeSql ) ; long normalizeStartTime = System . currentTimeMillis ( ) ; LOG . info ( "Normalizing stop sequences" ) ; // First get the column names ( to account for any non - standard fields that may be present )
List < String > columns = new ArrayList < > ( ) ; ResultSet resultSet = statement . executeQuery ( String . format ( "select * from %s limit 1" , tableToClone ) ) ; ResultSetMetaData metadata = resultSet . getMetaData ( ) ; int nColumns = metadata . getColumnCount ( ) ; for ( int i = 1 ; i <= nColumns ; i ++ ) { columns . add ( metadata . getColumnName ( i ) ) ; } // Replace stop sequence column with the normalized sequence values .
columns . set ( columns . indexOf ( "stop_sequence" ) , "-1 + row_number() over (partition by trip_id order by stop_sequence) as stop_sequence" ) ; String insertAllSql = String . format ( "insert into %s (select %s from %s)" , name , String . join ( ", " , columns ) , tableToClone ) ; LOG . info ( insertAllSql ) ; statement . execute ( insertAllSql ) ; LOG . info ( "Normalized stop times sequences in {} ms" , System . currentTimeMillis ( ) - normalizeStartTime ) ;


signature:
public class Table { /** * Make id column serial and set the next value based on the current max value . This is intended to operate on
 * existing statement / connection and should not be applied to a table has been created with a serial ( i . e . , auto -
 * incrementing ID . This code is derived from https : / / stackoverflow . com / a / 9490532/915811 */
private void applyAutoIncrementingSequence ( Statement statement ) throws SQLException { } }

implementation:
String selectMaxSql = String . format ( "SELECT MAX(id) + 1 FROM %s" , name ) ; int maxID = 0 ; LOG . info ( selectMaxSql ) ; statement . execute ( selectMaxSql ) ; ResultSet maxIdResult = statement . getResultSet ( ) ; if ( maxIdResult . next ( ) ) { maxID = maxIdResult . getInt ( 1 ) ; } // Set default max ID to 1 ( the start value cannot be less than MINVALUE 1)
// FIXME : Skip sequence creation if maxID = 1?
if ( maxID < 1 ) { maxID = 1 ; } String sequenceName = name + "_id_seq" ; String createSequenceSql = String . format ( "CREATE SEQUENCE %s START WITH %d" , sequenceName , maxID ) ; LOG . info ( createSequenceSql ) ; statement . execute ( createSequenceSql ) ; String alterColumnNextSql = String . format ( "ALTER TABLE %s ALTER COLUMN id SET DEFAULT nextval('%s')" , name , sequenceName ) ; LOG . info ( alterColumnNextSql ) ; statement . execute ( alterColumnNextSql ) ; String alterColumnNotNullSql = String . format ( "ALTER TABLE %s ALTER COLUMN id SET NOT NULL" , name ) ; LOG . info ( alterColumnNotNullSql ) ; statement . execute ( alterColumnNotNullSql ) ;


signature:
public class Table { /** * For an array of field headers , returns the matching set of { @ link Field } s for a { @ link Table } . If errorStorage is
 * not null , errors related to unexpected or duplicate header names will be stored . */
public Field [ ] getFieldsFromFieldHeaders ( String [ ] headers , SQLErrorStorage errorStorage ) { } }

implementation:
Field [ ] fields = new Field [ headers . length ] ; Set < String > fieldsSeen = new HashSet < > ( ) ; for ( int h = 0 ; h < headers . length ; h ++ ) { String header = sanitize ( headers [ h ] , errorStorage ) ; if ( fieldsSeen . contains ( header ) || "id" . equals ( header ) ) { // FIXME : add separate error for tables containing ID field .
if ( errorStorage != null ) errorStorage . storeError ( NewGTFSError . forTable ( this , DUPLICATE_HEADER ) . setBadValue ( header ) ) ; fields [ h ] = null ; } else { fields [ h ] = getFieldForName ( header ) ; fieldsSeen . add ( header ) ; } } return fields ;


signature:
public class Table { /** * Returns the index of the key field within the array of fields provided for a given table .
 * @ param fields array of fields ( intended to be derived from the headers of a csv text file ) */
public int getKeyFieldIndex ( Field [ ] fields ) { } }

implementation: String keyField = getKeyFieldName ( ) ; return Field . getFieldIndex ( fields , keyField ) ;


signature:
public class Util { /** * Generate a random unique prefix of n lowercase letters .
 * We can ' t count on sql table or schema names being case sensitive or tolerating ( leading ) digits .
 * For n = 10 , number of possibilities is 26 ^ 10 or 1.4E14.
 * The approximate probability of a hash collision is k ^ 2/2H where H is the number of possible hash values and
 * k is the number of items hashed .
 * SHA1 is 160 bits , MD5 is 128 bits , and UUIDs are 128 bits with only 122 actually random .
 * To reach the uniqueness of a UUID you need math . log ( 2 * * 122 , 26 ) or about 26 letters .
 * An MD5 can be represented as 32 hex digits so we don ' t save much length , but we do make it entirely alphabetical .
 * log base 2 of 26 is about 4.7 , so each character represents about 4.7 bits of randomness .
 * The question remains of whether we need globally unique IDs or just application - unique IDs . The downside of
 * generating IDs sequentially or with little randomness is that when multiple databases are created we ' ll have
 * feeds with the same IDs as older or other databases , allowing possible accidental collisions with strange
 * failure modes . */
public static String randomIdString ( ) { } }

implementation:
MersenneTwister twister = new MersenneTwister ( ) ; final int length = 27 ; char [ ] chars = new char [ length ] ; for ( int i = 0 ; i < length ; i ++ ) { chars [ i ] = ( char ) ( 'a' + twister . nextInt ( 26 ) ) ; } // Add a visual separator , which makes these easier to distinguish at a glance
chars [ 4 ] = '_' ; return new String ( chars ) ;


signature:
public class Util { /** * Ensures namespace is valid by ensuring it only has alphanumeric characters or the underscore
 * for all characters in the string
 * @ param namespace */
public static void ensureValidNamespace ( String namespace ) throws InvalidNamespaceException { } }

implementation: Pattern pattern = Pattern . compile ( "[^a-zA-Z0-9_\\.]" ) ; Matcher matcher = pattern . matcher ( namespace ) ; if ( matcher . find ( ) ) { throw new InvalidNamespaceException ( ) ; }


signature:
public class Field { /** * Finds the index of the field given a string name .
 * @ return the index of the field or - 1 if no match is found */
public static int getFieldIndex ( Field [ ] fields , String name ) { } }

implementation:
// Linear search , assuming a small number of fields per table .
for ( int i = 0 ; i < fields . length ; i ++ ) if ( fields [ i ] . name . equals ( name ) ) return i ; return - 1 ;


signature:
public class Field { /** * Get the expression used to select this column from the database based on the prefix . The csvOutput parameter is
 * needed in overriden method implementations that have special ways of outputting certain fields . The prefix
 * parameter is assumed to be either null or a string in the format : ` schema . ` */
public String getColumnExpression ( String prefix , boolean csvOutput ) { } }

implementation: return prefix != null ? String . format ( "%s%s" , prefix , name ) : name ;


signature:
public class ShapeMap { /** * We currently just expose them as immutable sets in RAM , since all of the modification operations are optional . */
@ Override public Set < Integer > keySet ( ) { } }

implementation:
// use a linkedhashset so values come out in order
Set < Integer > ret = new LinkedHashSet < > ( ) ; for ( Tuple2 < String , Integer > t : wrapped . keySet ( ) ) { ret . add ( t . b ) ; } // Don ' t let the user modify the set as it won ' t do what they expect ( change the map )
return Collections . unmodifiableSet ( ret ) ;


signature:
public class DateField { /** * DateField specific method to set a statement parameter from a { @ link LocalDate } . */
public Set < NewGTFSError > setParameter ( PreparedStatement preparedStatement , int oneBasedIndex , LocalDate localDate ) { } }

implementation: try { if ( localDate == null ) setNull ( preparedStatement , oneBasedIndex ) ; else preparedStatement . setString ( oneBasedIndex , localDate . format ( GTFS_DATE_FORMATTER ) ) ; return Collections . EMPTY_SET ; } catch ( Exception e ) { throw new StorageException ( e ) ; }


signature:
public class GTFSFeed { /** * The order in which we load the tables is important for two reasons .
 * 1 . We must load feed _ info first so we know the feed ID before loading any other entities . This could be relaxed
 * by having entities point to the feed object rather than its ID String .
 * 2 . Referenced entities must be loaded before any entities that reference them . This is because we check
 * referential integrity while the files are being loaded . This is done on the fly during loading because it allows
 * us to associate a line number with errors in objects that don ' t have any other clear identifier .
 * Interestingly , all references are resolvable when tables are loaded in alphabetical order . */
public void loadFromFile ( ZipFile zip , String fid ) throws Exception { } }

implementation:
if ( this . loaded ) throw new UnsupportedOperationException ( "Attempt to load GTFS into existing database" ) ; // NB we don ' t have a single CRC for the file , so we combine all the CRCs of the component files . NB we are not
// simply summing the CRCs because CRCs are ( I assume ) uniformly randomly distributed throughout the width of a
// long , so summing them is a convolution which moves towards a Gaussian with mean 0 ( i . e . more concentrated
// probability in the center ) , degrading the quality of the hash . Instead we XOR . Assuming each bit is independent ,
// this will yield a nice uniformly distributed result , because when combining two bits there is an equal
// probability of any input , which means an equal probability of any output . At least I think that ' s all correct .
// Repeated XOR is not commutative but zip . stream returns files in the order they are in the central directory
// of the zip file , so that ' s not a problem .
checksum = zip . stream ( ) . mapToLong ( ZipEntry :: getCrc ) . reduce ( ( l1 , l2 ) -> l1 ^ l2 ) . getAsLong ( ) ; db . getAtomicLong ( "checksum" ) . set ( checksum ) ; new FeedInfo . Loader ( this ) . loadTable ( zip ) ; // maybe we should just point to the feed object itself instead of its ID , and null out its stoptimes map after loading
if ( fid != null ) { feedId = fid ; LOG . info ( "Feed ID is undefined, pester maintainers to include a feed ID. Using file name {}." , feedId ) ; // TODO log an error , ideally feeds should include a feedID
} else if ( feedId == null || feedId . isEmpty ( ) ) { feedId = new File ( zip . getName ( ) ) . getName ( ) . replaceAll ( "\\.zip$" , "" ) ; LOG . info ( "Feed ID is undefined, pester maintainers to include a feed ID. Using file name {}." , feedId ) ; // TODO log an error , ideally feeds should include a feedID
} else { LOG . info ( "Feed ID is '{}'." , feedId ) ; } db . getAtomicString ( "feed_id" ) . set ( feedId ) ; new Agency . Loader ( this ) . loadTable ( zip ) ; // calendars and calendar dates are joined into services . This means a lot of manipulating service objects as
// they are loaded ; since mapdb keys / values are immutable , load them in memory then copy them to MapDB once
// we ' re done loading them
Map < String , Service > serviceTable = new HashMap < > ( ) ; new Calendar . Loader ( this , serviceTable ) . loadTable ( zip ) ; new CalendarDate . Loader ( this , serviceTable ) . loadTable ( zip ) ; this . services . putAll ( serviceTable ) ; serviceTable = null ; // free memory
// Same deal
Map < String , Fare > fares = new HashMap < > ( ) ; new FareAttribute . Loader ( this , fares ) . loadTable ( zip ) ; new FareRule . Loader ( this , fares ) . loadTable ( zip ) ; this . fares . putAll ( fares ) ; fares = null ; // free memory
new Route . Loader ( this ) . loadTable ( zip ) ; new ShapePoint . Loader ( this ) . loadTable ( zip ) ; new Stop . Loader ( this ) . loadTable ( zip ) ; new Transfer . Loader ( this ) . loadTable ( zip ) ; new Trip . Loader ( this ) . loadTable ( zip ) ; new Frequency . Loader ( this ) . loadTable ( zip ) ; new StopTime . Loader ( this ) . loadTable ( zip ) ; // comment out this line for quick testing using NL feed
LOG . info ( "{} errors" , errors . size ( ) ) ; for ( GTFSError error : errors ) { LOG . info ( "{}" , error ) ; } LOG . info ( "Building stop to stop times index" ) ; Bind . histogram ( stop_times , stopCountByStopTime , ( key , stopTime ) -> stopTime . stop_id ) ; Bind . secondaryKeys ( stop_times , stopStopTimeSet , ( key , stopTime ) -> new String [ ] { stopTime . stop_id } ) ; LOG . info ( "Building trips per service index" ) ; Bind . secondaryKeys ( trips , tripsPerService , ( key , trip ) -> new String [ ] { trip . service_id } ) ; LOG . info ( "Building services per date index" ) ; Bind . secondaryKeys ( services , servicesPerDate , ( key , service ) -> { LocalDate startDate = service . calendar != null ? service . calendar . start_date : service . calendar_dates . keySet ( ) . stream ( ) . sorted ( ) . findFirst ( ) . get ( ) ; LocalDate endDate = service . calendar != null ? service . calendar . end_date : service . calendar_dates . keySet ( ) . stream ( ) . sorted ( ) . reduce ( ( first , second ) -> second ) . get ( ) ; // end date for Period . between is not inclusive
int daysOfService = ( int ) ChronoUnit . DAYS . between ( startDate , endDate . plus ( 1 , ChronoUnit . DAYS ) ) ; return IntStream . range ( 0 , daysOfService ) . mapToObj ( offset -> startDate . plusDays ( offset ) ) . filter ( service :: activeOn ) . map ( date -> date . format ( dateFormatter ) ) . toArray ( size -> new String [ size ] ) ; } ) ; loaded = true ;


signature: public class GTFSFeed { public void validate ( boolean repair , Validator ... validators ) { } }

implementation:
long startValidation = System . currentTimeMillis ( ) ; for ( Validator validator : validators ) { try { long startValidator = System . currentTimeMillis ( ) ; // validator . validate ( this , repair ) ;
long endValidator = System . currentTimeMillis ( ) ; long diff = endValidator - startValidator ; LOG . info ( "{} finished in {} milliseconds." , validator . getClass ( ) . getSimpleName ( ) , TimeUnit . NANOSECONDS . toMillis ( diff ) ) ; } catch ( Exception e ) { LOG . error ( "Could not run {} validator." , validator . getClass ( ) . getSimpleName ( ) ) ; // LOG . error ( e . toString ( ) ) ;
e . printStackTrace ( ) ; } } long endValidation = System . nanoTime ( ) ; long total = endValidation - startValidation ; LOG . info ( "{} validators completed in {} milliseconds." , validators . length , TimeUnit . NANOSECONDS . toMillis ( total ) ) ;


signature:
public class GTFSFeed { /** * Static factory method returning a new instance of GTFSFeed containing the contents of
 * the GTFS file at the supplied filesystem path . Forces the feedId to the
 * supplied value if the parameter is non - null . */
public static GTFSFeed fromFile ( String file , String feedId ) { } }

implementation: GTFSFeed feed = new GTFSFeed ( ) ; ZipFile zip ; try { zip = new ZipFile ( file ) ; if ( feedId == null ) { feed . loadFromFile ( zip ) ; } else { feed . loadFromFile ( zip , feedId ) ; } zip . close ( ) ; return feed ; } catch ( Exception e ) { LOG . error ( "Error loading GTFS: {}" , e . getMessage ( ) ) ; throw new RuntimeException ( e ) ; }


signature:
public class GTFSFeed { /** * TODO rename getStopSpatialIndex to make it clear what the index contains . */
public STRtree getSpatialIndex ( ) { } }

implementation:
if ( this . spatialIndex == null ) { synchronized ( this ) { if ( this . spatialIndex == null ) { // build spatial index
STRtree stopIndex = new STRtree ( ) ; for ( Stop stop : this . stops . values ( ) ) { try { if ( Double . isNaN ( stop . stop_lat ) || Double . isNaN ( stop . stop_lon ) ) { continue ; } Coordinate stopCoord = new Coordinate ( stop . stop_lat , stop . stop_lon ) ; stopIndex . insert ( new Envelope ( stopCoord ) , stop ) ; } catch ( Exception e ) { e . printStackTrace ( ) ; } } try { stopIndex . build ( ) ; this . spatialIndex = stopIndex ; } catch ( Exception e ) { e . printStackTrace ( ) ; } } } } return this . spatialIndex ;


signature:
public class GTFSFeed { /** * MapDB - based implementation to find patterns .
 * FIXME : Remove and make pattern finding happen during validation ? We want to share the pattern finder between the
 * two implementations ( MapDB and RDBMS ) , apply the same validation process to both kinds of storage , and produce
 * Patterns in the same way in both cases , during validation . This prevents us from iterating over every stopTime
 * twice , since we ' re already iterating over all of them in validation . However , in this case it might not be costly
 * to simply retrieve the stop times from the stop _ times map . */
public void findPatterns ( ) { } }

implementation:
PatternFinder patternFinder = new PatternFinder ( ) ; // Iterate over trips and process each trip and its stop times .
for ( Trip trip : this . trips . values ( ) ) { Iterable < StopTime > orderedStopTimesForTrip = this . getOrderedStopTimesForTrip ( trip . trip_id ) ; patternFinder . processTrip ( trip , orderedStopTimesForTrip ) ; } Map < TripPatternKey , Pattern > patternObjects = patternFinder . createPatternObjects ( this . stops , null ) ; this . patterns . putAll ( patternObjects . values ( ) . stream ( ) . collect ( Collectors . toMap ( Pattern :: getId , pattern -> pattern ) ) ) ;


signature:
public class GTFSFeed { /** * For the given trip ID , fetch all the stop times in order , and interpolate stop - to - stop travel times . */
public Iterable < StopTime > getInterpolatedStopTimesForTrip ( String trip_id ) throws FirstAndLastStopsDoNotHaveTimes { } }

implementation:
// clone stop times so as not to modify base GTFS structures
StopTime [ ] stopTimes = StreamSupport . stream ( getOrderedStopTimesForTrip ( trip_id ) . spliterator ( ) , false ) . map ( st -> st . clone ( ) ) . toArray ( i -> new StopTime [ i ] ) ; // avoid having to make sure that the array has length below .
if ( stopTimes . length == 0 ) return Collections . emptyList ( ) ; // first pass : set all partially filled stop times
for ( StopTime st : stopTimes ) { if ( st . arrival_time != Entity . INT_MISSING && st . departure_time == Entity . INT_MISSING ) { st . departure_time = st . arrival_time ; } if ( st . arrival_time == Entity . INT_MISSING && st . departure_time != Entity . INT_MISSING ) { st . arrival_time = st . departure_time ; } } // quick check : ensure that first and last stops have times .
// technically GTFS requires that both arrival _ time and departure _ time be filled at both the first and last stop ,
// but we are slightly more lenient and only insist that one of them be filled at both the first and last stop .
// The meaning of the first stop ' s arrival time is unclear , and same for the last stop ' s departure time ( except
// in the case of interlining ) .
// it ' s fine to just check departure time , as the above pass ensures that all stop times have either both
// arrival and departure times , or neither
if ( stopTimes [ 0 ] . departure_time == Entity . INT_MISSING || stopTimes [ stopTimes . length - 1 ] . departure_time == Entity . INT_MISSING ) { throw new FirstAndLastStopsDoNotHaveTimes ( ) ; } // second pass : fill complete stop times
int startOfInterpolatedBlock = - 1 ; for ( int stopTime = 0 ; stopTime < stopTimes . length ; stopTime ++ ) { if ( stopTimes [ stopTime ] . departure_time == Entity . INT_MISSING && startOfInterpolatedBlock == - 1 ) { startOfInterpolatedBlock = stopTime ; } else if ( stopTimes [ stopTime ] . departure_time != Entity . INT_MISSING && startOfInterpolatedBlock != - 1 ) { // we have found the end of the interpolated section
int nInterpolatedStops = stopTime - startOfInterpolatedBlock ; double totalLengthOfInterpolatedSection = 0 ; double [ ] lengthOfInterpolatedSections = new double [ nInterpolatedStops ] ; GeodeticCalculator calc = new GeodeticCalculator ( ) ; for ( int stopTimeToInterpolate = startOfInterpolatedBlock , i = 0 ; stopTimeToInterpolate < stopTime ; stopTimeToInterpolate ++ , i ++ ) { Stop start = stops . get ( stopTimes [ stopTimeToInterpolate - 1 ] . stop_id ) ; Stop end = stops . get ( stopTimes [ stopTimeToInterpolate ] . stop_id ) ; calc . setStartingGeographicPoint ( start . stop_lon , start . stop_lat ) ; calc . setDestinationGeographicPoint ( end . stop_lon , end . stop_lat ) ; double segLen = calc . getOrthodromicDistance ( ) ; totalLengthOfInterpolatedSection += segLen ; lengthOfInterpolatedSections [ i ] = segLen ; } // add the segment post - last - interpolated - stop
Stop start = stops . get ( stopTimes [ stopTime - 1 ] . stop_id ) ; Stop end = stops . get ( stopTimes [ stopTime ] . stop_id ) ; calc . setStartingGeographicPoint ( start . stop_lon , start . stop_lat ) ; calc . setDestinationGeographicPoint ( end . stop_lon , end . stop_lat ) ; totalLengthOfInterpolatedSection += calc . getOrthodromicDistance ( ) ; int departureBeforeInterpolation = stopTimes [ startOfInterpolatedBlock - 1 ] . departure_time ; int arrivalAfterInterpolation = stopTimes [ stopTime ] . arrival_time ; int totalTime = arrivalAfterInterpolation - departureBeforeInterpolation ; double lengthSoFar = 0 ; for ( int stopTimeToInterpolate = startOfInterpolatedBlock , i = 0 ; stopTimeToInterpolate < stopTime ; stopTimeToInterpolate ++ , i ++ ) { lengthSoFar += lengthOfInterpolatedSections [ i ] ; int time = ( int ) ( departureBeforeInterpolation + totalTime * ( lengthSoFar / totalLengthOfInterpolatedSection ) ) ; stopTimes [ stopTimeToInterpolate ] . arrival_time = stopTimes [ stopTimeToInterpolate ] . departure_time = time ; } // we ' re done with this block
startOfInterpolatedBlock = - 1 ; } } return Arrays . asList ( stopTimes ) ;


signature:
public class GTFSFeed { /** * Get the length of a trip in meters . */
public double getTripDistance ( String trip_id , boolean straightLine ) { } }

implementation: return straightLine ? GeoUtils . getDistance ( this . getStraightLineForStops ( trip_id ) ) : GeoUtils . getDistance ( this . getTripGeometry ( trip_id ) ) ;


signature:
public class GTFSFeed { /** * Get trip speed in meters per second . */
public double getTripSpeed ( String trip_id , boolean straightLine ) { } }

implementation:
StopTime firstStopTime = this . stop_times . ceilingEntry ( Fun . t2 ( trip_id , null ) ) . getValue ( ) ; StopTime lastStopTime = this . stop_times . floorEntry ( Fun . t2 ( trip_id , Fun . HI ) ) . getValue ( ) ; // ensure that stopTime returned matches trip id ( i . e . , that the trip has stoptimes )
if ( ! firstStopTime . trip_id . equals ( trip_id ) || ! lastStopTime . trip_id . equals ( trip_id ) ) { return Double . NaN ; } double distance = getTripDistance ( trip_id , straightLine ) ; // trip time ( in seconds )
int time = lastStopTime . arrival_time - firstStopTime . departure_time ; return distance / time ; // meters per second


signature:
public class GTFSFeed { /** * Get list of stop _ times for a given stop _ id . */
public List < StopTime > getStopTimesForStop ( String stop_id ) { } }

implementation: SortedSet < Tuple2 < String , Tuple2 > > index = this . stopStopTimeSet . subSet ( new Tuple2 < > ( stop_id , null ) , new Tuple2 ( stop_id , Fun . HI ) ) ; return index . stream ( ) . map ( tuple -> this . stop_times . get ( tuple . b ) ) . collect ( Collectors . toList ( ) ) ;


signature:
public class GTFSFeed { /** * Get list of services for each date of service . */
public List < Service > getServicesForDate ( LocalDate date ) { } }

implementation: String dateString = date . format ( dateFormatter ) ; SortedSet < Tuple2 < String , String > > index = this . servicesPerDate . subSet ( new Tuple2 < > ( dateString , null ) , new Tuple2 ( dateString , Fun . HI ) ) ; return index . stream ( ) . map ( tuple -> this . services . get ( tuple . b ) ) . collect ( Collectors . toList ( ) ) ;


signature:
public class GTFSFeed { /** * Get list of distinct trips ( filters out multiple visits by a trip ) a given stop _ id . */
public List < Trip > getDistinctTripsForStop ( String stop_id ) { } }

implementation: return getStopTimesForStop ( stop_id ) . stream ( ) . map ( stopTime -> this . trips . get ( stopTime . trip_id ) ) . distinct ( ) . collect ( Collectors . toList ( ) ) ;


signature:
public class GTFSFeed { /** * Get the likely time zone for a stop using the agency of the first stop time encountered for the stop . */
public ZoneId getAgencyTimeZoneForStop ( String stop_id ) { } }

implementation: StopTime stopTime = getStopTimesForStop ( stop_id ) . iterator ( ) . next ( ) ; Trip trip = this . trips . get ( stopTime . trip_id ) ; Route route = this . routes . get ( trip . route_id ) ; Agency agency = route . agency_id != null ? this . agency . get ( route . agency_id ) : this . agency . get ( 0 ) ; return ZoneId . of ( agency . agency_timezone ) ;


signature:
public class GTFSFeed { /** * TODO : code review */
public Geometry getMergedBuffers ( ) { } }

implementation:
if ( this . mergedBuffers == null ) { // synchronized ( this ) {
Collection < Geometry > polygons = new ArrayList < > ( ) ; for ( Stop stop : this . stops . values ( ) ) { if ( getStopTimesForStop ( stop . stop_id ) . isEmpty ( ) ) { continue ; } if ( stop . stop_lat > - 1 && stop . stop_lat < 1 || stop . stop_lon > - 1 && stop . stop_lon < 1 ) { continue ; } Point stopPoint = gf . createPoint ( new Coordinate ( stop . stop_lon , stop . stop_lat ) ) ; Polygon stopBuffer = ( Polygon ) stopPoint . buffer ( .01 ) ; polygons . add ( stopBuffer ) ; } Geometry multiGeometry = gf . buildGeometry ( polygons ) ; this . mergedBuffers = multiGeometry . union ( ) ; if ( polygons . size ( ) > 100 ) { this . mergedBuffers = DouglasPeuckerSimplifier . simplify ( this . mergedBuffers , .001 ) ; } } return this . mergedBuffers ;


signature:
public class FareAttribute { /** * Sets the parameters for a prepared statement following the parameter order defined in
 * { @ link com . conveyal . gtfs . loader . Table # FARE _ ATTRIBUTES } . 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 . setDouble ( oneBasedIndex ++ , price ) ; statement . setString ( oneBasedIndex ++ , currency_type ) ; setIntParameter ( statement , oneBasedIndex ++ , payment_method ) ; // FIXME Entity . INT _ MISSING causing out of range error on small int
setIntParameter ( statement , oneBasedIndex ++ , transfers ) ; statement . setString ( oneBasedIndex ++ , agency_id ) ; setIntParameter ( statement , oneBasedIndex ++ , transfer_duration ) ;


signature:
public class Geometries { /** * Taken from geofabrik . de country boundary KML , because our OSM PBF files use this same boundary . */
public static Geometry getNetherlands ( ) { } }


signature: public class PatternFinder { public void processTrip ( Trip trip , Iterable < StopTime > orderedStopTimes ) { } }

implementation:
if ( ++ nTripsProcessed % 100000 == 0 ) { LOG . info ( "trip {}" , human ( nTripsProcessed ) ) ; } // No need to scope the route ID here , patterns are built within the context of a single feed .
// Create a key that might already be in the map ( by semantic equality )
TripPatternKey key = new TripPatternKey ( trip . route_id ) ; for ( StopTime st : orderedStopTimes ) { key . addStopTime ( st ) ; } // Add the current trip to the map , possibly extending an existing list of trips on this pattern .
tripsForPattern . put ( key , trip ) ;


signature:
public class PatternFinder { /** * Once all trips have been processed , call this method to produce the final Pattern objects representing all the
 * unique sequences of stops encountered . Returns map of patterns to their keys so that downstream functions can
 * make use of trip pattern keys for constructing pattern stops or other derivative objects . */
public Map < TripPatternKey , Pattern > createPatternObjects ( Map < String , Stop > stopById , SQLErrorStorage errorStorage ) { } }

implementation:
// Make pattern ID one - based to avoid any JS type confusion between an ID of zero vs . null value .
int nextPatternId = 1 ; // Create an in - memory list of Patterns because we will later rename them before inserting them into storage .
Map < TripPatternKey , Pattern > patterns = new HashMap < > ( ) ; // TODO assign patterns sequential small integer IDs ( may include route )
for ( TripPatternKey key : tripsForPattern . keySet ( ) ) { Collection < Trip > trips = tripsForPattern . get ( key ) ; Pattern pattern = new Pattern ( key . stops , trips , null ) ; // Overwrite long UUID with sequential integer pattern ID
pattern . pattern_id = Integer . toString ( nextPatternId ++ ) ; // FIXME : Should associated shapes be a single entry ?
pattern . associatedShapes = new HashSet < > ( ) ; trips . stream ( ) . forEach ( trip -> pattern . associatedShapes . add ( trip . shape_id ) ) ; if ( pattern . associatedShapes . size ( ) > 1 && errorStorage != null ) { // Store an error if there is more than one shape per pattern . Note : error storage is null if called via
// MapDB implementation .
// TODO : Should shape ID be added to trip pattern key ?
errorStorage . storeError ( NewGTFSError . forEntity ( pattern , NewGTFSErrorType . MULTIPLE_SHAPES_FOR_PATTERN ) . setBadValue ( pattern . associatedShapes . toString ( ) ) ) ; } patterns . put ( key , pattern ) ; } // Name patterns before storing in SQL database .
renamePatterns ( patterns . values ( ) , stopById ) ; LOG . info ( "Total patterns: {}" , tripsForPattern . keySet ( ) . size ( ) ) ; return patterns ;


signature:
public class PatternFinder { /** * Destructively rename the supplied collection of patterns .
 * This process requires access to all the stops in the feed .
 * Some validators already cache a map of all the stops . There ' s probably a cleaner way to do this . */
public static void renamePatterns ( Collection < Pattern > patterns , Map < String , Stop > stopById ) { } }

implementation:
LOG . info ( "Generating unique names for patterns" ) ; Map < String , PatternNamingInfo > namingInfoForRoute = new HashMap < > ( ) ; for ( Pattern pattern : patterns ) { if ( pattern . associatedTrips . isEmpty ( ) || pattern . orderedStops . isEmpty ( ) ) continue ; // Each pattern within a route has a unique name ( within that route , not across the entire feed )
PatternNamingInfo namingInfo = namingInfoForRoute . get ( pattern . route_id ) ; if ( namingInfo == null ) { namingInfo = new PatternNamingInfo ( ) ; namingInfoForRoute . put ( pattern . route_id , namingInfo ) ; } // Pattern names are built using stop names rather than stop IDs .
// Stop names , unlike IDs , are not guaranteed to be unique .
// Therefore we must track used names carefully to avoid duplicates .
String fromName = stopById . get ( pattern . orderedStops . get ( 0 ) ) . stop_name ; String toName = stopById . get ( pattern . orderedStops . get ( pattern . orderedStops . size ( ) - 1 ) ) . stop_name ; namingInfo . fromStops . put ( fromName , pattern ) ; namingInfo . toStops . put ( toName , pattern ) ; for ( String stopId : pattern . orderedStops ) { Stop stop = stopById . get ( stopId ) ; if ( fromName . equals ( stop . stop_name ) || toName . equals ( stop . stop_name ) ) continue ; namingInfo . vias . put ( stop . stop_name , pattern ) ; } namingInfo . patternsOnRoute . add ( pattern ) ; } // name the patterns on each route
for ( PatternNamingInfo info : namingInfoForRoute . values ( ) ) { for ( Pattern pattern : info . patternsOnRoute ) { pattern . name = null ; // clear this now so we don ' t get confused later on
String fromName = stopById . get ( pattern . orderedStops . get ( 0 ) ) . stop_name ; String toName = stopById . get ( pattern . orderedStops . get ( pattern . orderedStops . size ( ) - 1 ) ) . stop_name ; // check if combination from , to is unique
Set < Pattern > intersection = new HashSet < > ( info . fromStops . get ( fromName ) ) ; intersection . retainAll ( info . toStops . get ( toName ) ) ; if ( intersection . size ( ) == 1 ) { pattern . name = String . format ( Locale . US , "from %s to %s" , fromName , toName ) ; continue ; } // check for unique via stop
pattern . orderedStops . stream ( ) . map ( stopById :: get ) . forEach ( stop -> { Set < Pattern > viaIntersection = new HashSet < > ( intersection ) ; viaIntersection . retainAll ( info . vias . get ( stop . stop_name ) ) ; if ( viaIntersection . size ( ) == 1 ) { pattern . name = String . format ( Locale . US , "from %s to %s via %s" , fromName , toName , stop . stop_name ) ; } } ) ; if ( pattern . name == null ) { // no unique via , one pattern is subset of other .
if ( intersection . size ( ) == 2 ) { Iterator < Pattern > it = intersection . iterator ( ) ; Pattern p0 = it . next ( ) ; Pattern p1 = it . next ( ) ; if ( p0 . orderedStops . size ( ) > p1 . orderedStops . size ( ) ) { p1 . name = String . format ( Locale . US , "from %s to %s express" , fromName , toName ) ; p0 . name = String . format ( Locale . US , "from %s to %s local" , fromName , toName ) ; } else if ( p1 . orderedStops . size ( ) > p0 . orderedStops . size ( ) ) { p0 . name = String . format ( Locale . US , "from %s to %s express" , fromName , toName ) ; p1 . name = String . format ( Locale . US , "from %s to %s local" , fromName , toName ) ; } } } if ( pattern . name == null ) { // give up
pattern . name = String . format ( Locale . US , "from %s to %s like trip %s" , fromName , toName , pattern . associatedTrips . get ( 0 ) ) ; } } // attach a stop and trip count to each
for ( Pattern pattern : info . patternsOnRoute ) { pattern . name = String . format ( Locale . US , "%s stops %s (%s trips)" , pattern . orderedStops . size ( ) , pattern . name , pattern . associatedTrips . size ( ) ) ; } }


signature:
public class Stop { /** * Sets the parameters for a prepared statement following the parameter order defined in
 * { @ link com . conveyal . gtfs . loader . Table # STOPS } . 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 ) ; int wheelchairBoarding = 0 ; try { wheelchairBoarding = Integer . parseInt ( wheelchair_boarding ) ; } catch ( NumberFormatException e ) { // Do nothing , wheelchairBoarding will remain zero .
} statement . setString ( oneBasedIndex ++ , stop_id ) ; statement . setString ( oneBasedIndex ++ , stop_code ) ; statement . setString ( oneBasedIndex ++ , stop_name ) ; statement . setString ( oneBasedIndex ++ , stop_desc ) ; statement . setDouble ( oneBasedIndex ++ , stop_lat ) ; statement . setDouble ( oneBasedIndex ++ , stop_lon ) ; statement . setString ( oneBasedIndex ++ , zone_id ) ; statement . setString ( oneBasedIndex ++ , stop_url != null ? stop_url . toString ( ) : null ) ; setIntParameter ( statement , oneBasedIndex ++ , location_type ) ; statement . setString ( oneBasedIndex ++ , parent_station ) ; statement . setString ( oneBasedIndex ++ , stop_timezone ) ; // FIXME : For some reason wheelchair boarding type is String
setIntParameter ( statement , oneBasedIndex ++ , wheelchairBoarding ) ;


signature:
public class ColorField { /** * Check that a string can be properly parsed and is in range . */
public ValidateFieldResult < String > validateAndConvert ( String string ) { } }

implementation:
ValidateFieldResult < String > result = new ValidateFieldResult < > ( string ) ; try { if ( string . length ( ) != 6 ) { result . errors . add ( NewGTFSError . forFeed ( NewGTFSErrorType . COLOR_FORMAT , string ) ) ; } int integer = Integer . parseInt ( string , 16 ) ; return result ; // Could also store the integer .
} catch ( Exception ex ) { throw new StorageException ( NewGTFSErrorType . COLOR_FORMAT , string ) ; }


signature:
public class PatternFinderValidator { /** * Store patterns and pattern stops in the database . Also , update the trips table with a pattern _ id column . */
@ Override public void complete ( ValidationResult validationResult ) { } }

implementation:
LOG . info ( "Finding patterns..." ) ; // FIXME : There may be a better way to handle getting the full list of stops
Map < String , Stop > stopById = new HashMap < > ( ) ; for ( Stop stop : feed . stops ) { stopById . put ( stop . stop_id , stop ) ; } // FIXME In the editor we need patterns to exist separately from and before trips themselves , so me make another table .
Map < TripPatternKey , Pattern > patterns = patternFinder . createPatternObjects ( stopById , errorStorage ) ; Connection connection = null ; try { // TODO this assumes gtfs - lib is using an SQL database and not a MapDB .
// Maybe we should just create patterns in a separate step , but that would mean iterating over the
// stop _ times twice .
LOG . info ( "Creating pattern and pattern stops tables." ) ; connection = feed . getConnection ( ) ; Statement statement = connection . createStatement ( ) ; String tripsTableName = feed . tablePrefix + "trips" ; String patternsTableName = feed . tablePrefix + "patterns" ; String patternStopsTableName = feed . tablePrefix + "pattern_stops" ; statement . execute ( String . format ( "alter table %s add column pattern_id varchar" , tripsTableName ) ) ; // FIXME : Here we ' re creating a pattern table that has an integer ID field ( similar to the other GTFS tables )
// AND a varchar pattern _ id with essentially the same value cast to a string . Perhaps the pattern ID should
// be a UUID or something , just to better distinguish it from the int ID ?
Table patternsTable = new Table ( patternsTableName , Pattern . class , Requirement . EDITOR , Table . PATTERNS . fields ) ; Table patternStopsTable = new Table ( patternStopsTableName , PatternStop . class , Requirement . EDITOR , Table . PATTERN_STOP . fields ) ; // Create pattern and pattern stops table , each with serial ID fields .
patternsTable . createSqlTable ( connection , null , true ) ; patternStopsTable . createSqlTable ( connection , null , true ) ; // Generate prepared statements for inserts .
String insertPatternSql = patternsTable . generateInsertSql ( true ) ; String insertPatternStopSql = patternStopsTable . generateInsertSql ( true ) ; PreparedStatement insertPatternStatement = connection . prepareStatement ( insertPatternSql ) ; BatchTracker patternTracker = new BatchTracker ( "pattern" , insertPatternStatement ) ; PreparedStatement insertPatternStopStatement = connection . prepareStatement ( insertPatternStopSql ) ; BatchTracker patternStopTracker = new BatchTracker ( "pattern stop" , insertPatternStopStatement ) ; int currentPatternIndex = 0 ; LOG . info ( "Storing patterns and pattern stops" ) ; // If using Postgres , load pattern to trips mapping into temp table for quick updating .
boolean postgresText = ( connection . getMetaData ( ) . getDatabaseProductName ( ) . equals ( "PostgreSQL" ) ) ; if ( postgresText ) { // NOTE : temp table name must NOT be prefixed with schema because temp tables are prefixed with their own
// connection - unique schema .
tempPatternForTripsTable = "pattern_for_trips" ; tempPatternForTripsTextFile = File . createTempFile ( tempPatternForTripsTable , "text" ) ; LOG . info ( "Loading via temporary text file at {}" , tempPatternForTripsTextFile . getAbsolutePath ( ) ) ; // Create temp table for updating trips with pattern IDs to be dropped at the end of the transaction .
String createTempSql = String . format ( "create temp table %s(trip_id varchar, pattern_id varchar) on commit drop" , tempPatternForTripsTable ) ; LOG . info ( createTempSql ) ; statement . execute ( createTempSql ) ; patternForTripsFileStream = new PrintStream ( new BufferedOutputStream ( new FileOutputStream ( tempPatternForTripsTextFile ) ) ) ; } for ( Map . Entry < TripPatternKey , Pattern > entry : patterns . entrySet ( ) ) { Pattern pattern = entry . getValue ( ) ; LOG . debug ( "Batching pattern {}" , pattern . pattern_id ) ; TripPatternKey key = entry . getKey ( ) ; pattern . setStatementParameters ( insertPatternStatement , true ) ; patternTracker . addBatch ( ) ; // Construct pattern stops based on values in trip pattern key .
// FIXME : Use pattern stops table here ?
int lastValidDeparture = key . departureTimes . get ( 0 ) ; for ( int i = 0 ; i < key . stops . size ( ) ; i ++ ) { int travelTime = 0 ; String stopId = key . stops . get ( i ) ; int arrival = key . arrivalTimes . get ( i ) ; if ( i > 0 ) { int prevDeparture = key . departureTimes . get ( i - 1 ) ; // Set travel time for all stops except the first .
if ( prevDeparture != INT_MISSING ) { // Update the previous departure if it ' s not missing . Otherwise , base travel time based on the
// most recent valid departure .
lastValidDeparture = prevDeparture ; } travelTime = arrival == INT_MISSING || lastValidDeparture == INT_MISSING ? INT_MISSING : arrival - lastValidDeparture ; } int departure = key . departureTimes . get ( i ) ; int dwellTime = arrival == INT_MISSING || departure == INT_MISSING ? INT_MISSING : departure - arrival ; insertPatternStopStatement . setString ( 1 , pattern . pattern_id ) ; // Stop sequence is zero - based .
setIntParameter ( insertPatternStopStatement , 2 , i ) ; insertPatternStopStatement . setString ( 3 , stopId ) ; setIntParameter ( insertPatternStopStatement , 4 , travelTime ) ; setIntParameter ( insertPatternStopStatement , 5 , dwellTime ) ; setIntParameter ( insertPatternStopStatement , 6 , key . dropoffTypes . get ( i ) ) ; setIntParameter ( insertPatternStopStatement , 7 , key . pickupTypes . get ( i ) ) ; setDoubleParameter ( insertPatternStopStatement , 8 , key . shapeDistances . get ( i ) ) ; setIntParameter ( insertPatternStopStatement , 9 , key . timepoints . get ( i ) ) ; patternStopTracker . addBatch ( ) ; } // Finally , update all trips on this pattern to reference this pattern ' s ID .
String questionMarks = String . join ( ", " , Collections . nCopies ( pattern . associatedTrips . size ( ) , "?" ) ) ; PreparedStatement updateTripStatement = connection . prepareStatement ( String . format ( "update %s set pattern_id = ? where trip_id in (%s)" , tripsTableName , questionMarks ) ) ; int oneBasedIndex = 1 ; updateTripStatement . setString ( oneBasedIndex ++ , pattern . pattern_id ) ; // Prepare each trip in pattern to update trips table .
for ( String tripId : pattern . associatedTrips ) { if ( postgresText ) { // Add line to temp csv file if using postgres .
// No need to worry about null trip IDs because the trips have already been processed .
String [ ] strings = new String [ ] { tripId , pattern . pattern_id } ; // Print a new line in the standard postgres text format :
// https : / / www . postgresql . org / docs / 9.1 / static / sql - copy . html # AEN64380
patternForTripsFileStream . println ( String . join ( "\t" , strings ) ) ; } else { // Otherwise , set statement parameter .
updateTripStatement . setString ( oneBasedIndex ++ , tripId ) ; } } if ( ! postgresText ) { // Execute trip update statement if not using temp text file .
LOG . info ( "Updating {} trips with pattern ID {} (%d/%d)" , pattern . associatedTrips . size ( ) , pattern . pattern_id , currentPatternIndex , patterns . size ( ) ) ; updateTripStatement . executeUpdate ( ) ; } currentPatternIndex += 1 ; } // Send any remaining prepared statement calls to the database backend .
patternTracker . executeRemaining ( ) ; patternStopTracker . executeRemaining ( ) ; LOG . info ( "Done storing patterns and pattern stops." ) ; if ( postgresText ) { // Finally , copy the pattern for trips text file into a table , create an index on trip IDs , and update
// the trips table .
LOG . info ( "Updating trips with pattern IDs" ) ; patternForTripsFileStream . close ( ) ; // Copy file contents into temp pattern for trips table .
copyFromFile ( connection , tempPatternForTripsTextFile , tempPatternForTripsTable ) ; // Before updating the trips with pattern IDs , index the table on trip _ id .
String patternForTripsIndexSql = String . format ( "create index temp_trips_pattern_id_idx on %s (trip_id)" , tempPatternForTripsTable ) ; LOG . info ( patternForTripsIndexSql ) ; statement . execute ( patternForTripsIndexSql ) ; // Finally , execute the update statement .
String updateTripsSql = String . format ( "update %s set pattern_id = %s.pattern_id from %s where %s.trip_id = %s.trip_id" , tripsTableName , tempPatternForTripsTable , tempPatternForTripsTable , tripsTableName , tempPatternForTripsTable ) ; LOG . info ( updateTripsSql ) ; statement . executeUpdate ( updateTripsSql ) ; // Delete temp file . Temp table will be dropped after the transaction is committed .
tempPatternForTripsTextFile . delete ( ) ; LOG . info ( "Updating trips complete" ) ; } LOG . info ( "Creating index on patterns" ) ; statement . executeUpdate ( String . format ( "alter table %s add primary key (pattern_id)" , patternsTableName ) ) ; LOG . info ( "Creating index on pattern stops" ) ; statement . executeUpdate ( String . format ( "alter table %s add primary key (pattern_id, stop_sequence)" , patternStopsTableName ) ) ; // Index new pattern _ id column on trips . The other tables are already indexed because they have primary keys .
LOG . info ( "Indexing trips on pattern id." ) ; statement . execute ( String . format ( "create index trips_pattern_id_idx on %s (pattern_id)" , tripsTableName ) ) ; LOG . info ( "Done indexing." ) ; connection . commit ( ) ; } catch ( SQLException | IOException e ) { // Rollback transaction if failure occurs on creating patterns .
DbUtils . rollbackAndCloseQuietly ( connection ) ; // This exception will be stored as a validator failure .
throw new RuntimeException ( e ) ; } finally { // Close transaction finally .
DbUtils . closeQuietly ( connection ) ; }


signature:
public class URLField { /** * Check that a string can be properly parsed and is in range . */
public ValidateFieldResult < String > validateAndConvert ( String string ) { } }

implementation:
ValidateFieldResult < String > result = cleanString ( string ) ; try { new URL ( result . clean ) ; // TODO call this to validate , but we can ' t default to zero
return result ; } catch ( Exception ex ) { result . errors . add ( NewGTFSError . forFeed ( NewGTFSErrorType . URL_FORMAT , string ) ) ; return result ; }


signature:
public class IdleReceiver { /** * We can ' t get idle broadcasts while the app is not running . Since our app will probably be
 * killed before the device becomes idle , let ' s set an alarm sometime in the future that will
 * force an idle check . */
private static void setAnywayTimer ( Context context ) { } }

implementation: AlarmManager am = ( AlarmManager ) context . getSystemService ( Context . ALARM_SERVICE ) ; Intent intent = new Intent ( context , IdleReceiver . class ) ; intent . setAction ( ACTION_TRIGGER_IDLE ) ; PendingIntent pendingIntent = PendingIntent . getBroadcast ( context , 0 , intent , 0 ) ; final long nowElapsed = SystemClock . elapsedRealtime ( ) ; final long when = nowElapsed + INACTIVITY_ANYWAY_THRESHOLD ; compatSetWindow ( am , AlarmManager . ELAPSED_REALTIME_WAKEUP , when , IDLE_WINDOW_SLOP , pendingIntent ) ;


signature:
public class IdleReceiver { /** * Call this as soon as we get a chance , it will be unregistered whenever our app is killed . */
public static void enableReceiver ( Context context ) { } }

implementation: IntentFilter filter = getFilter ( ) ; sReceiver = new IdleReceiver ( ) ; context . getApplicationContext ( ) . registerReceiver ( sReceiver , filter ) ;


signature:
public class IdleReceiver { /** * Interaction with the task manager service */
void reportNewIdleState ( Context context , boolean isIdle ) { } }

implementation: JobStore jobStore = JobStore . initAndGet ( context ) ; synchronized ( jobStore ) { ArraySet < JobStatus > jobs = jobStore . getJobs ( ) ; for ( int i = 0 ; i < jobs . size ( ) ; i ++ ) { JobStatus ts = jobs . valueAt ( i ) ; ts . idleConstraintSatisfied . set ( isIdle ) ; } } startWakefulService ( context , JobServiceCompat . maybeRunJobs ( context ) ) ;


signature:
public class JobStore { /** * Add a job to the master list , persisting it if necessary . If the JobStatus already exists ,
 * it will be replaced .
 * @ param jobStatus Job to add .
 * @ return Whether or not an equivalent JobStatus was replaced by this operation . */
public boolean add ( JobStatus jobStatus ) { } }

implementation: boolean replaced = mJobSet . remove ( jobStatus ) ; mJobSet . add ( jobStatus ) ; if ( ! jobStatus . isPersisted ( ) ) { markForBootSession ( jobStatus ) ; } maybeWriteStatusToDiskAsync ( ) ; return replaced ;


signature:
public class JobStore { /** * Whether this jobStatus object already exists in the JobStore . */
public boolean containsJobId ( int jobId ) { } }

implementation: for ( int i = mJobSet . size ( ) - 1 ; i >= 0 ; i -- ) { JobStatus ts = mJobSet . valueAt ( i ) ; if ( ts . matches ( jobId ) ) { return true ; } } return false ;


signature:
public class JobStore { /** * Remove the provided job . Will also delete the job if it was persisted .
 * @ return Whether or not the job existed to be removed . */
public boolean remove ( JobStatus jobStatus ) { } }

implementation: boolean removed = mJobSet . remove ( jobStatus ) ; if ( ! removed ) { return false ; } if ( ! jobStatus . isPersisted ( ) ) { unmarkForBootSession ( jobStatus ) ; } maybeWriteStatusToDiskAsync ( ) ; return removed ;


signature:
public class JobSchedulerService { /** * A job is rescheduled with exponential back - off if the client requests this from their
 * execution logic .
 * A caveat is for idle - mode jobs , for which the idle - mode constraint will usurp the
 * timeliness of the reschedule . For an idle - mode job , no deadline is given .
 * @ return A newly instantiated JobStatus with the same constraints as the last job except
 * with adjusted timing constraints . */
private JobStatus rescheduleFailedJob ( JobStatus job ) { } }

implementation:
if ( job . hasIdleConstraint ( ) ) { // Don ' t need to modify time on idle job , it will run whenever the next idle period is .
return job ; } final long elapsedNowMillis = SystemClock . elapsedRealtime ( ) ; final JobInfo jobInfo = job . getJob ( ) ; final long initialBackoffMillis = jobInfo . getInitialBackoffMillis ( ) ; final int backoffAttemps = job . getNumFailures ( ) + 1 ; long delayMillis ; switch ( job . getJob ( ) . getBackoffPolicy ( ) ) { case JobInfo . BACKOFF_POLICY_LINEAR : delayMillis = initialBackoffMillis * backoffAttemps ; break ; default : case JobInfo . BACKOFF_POLICY_EXPONENTIAL : delayMillis = ( long ) Math . scalb ( initialBackoffMillis , backoffAttemps - 1 ) ; break ; } delayMillis = Math . min ( delayMillis , JobInfo . MAX_BACKOFF_DELAY_MILLIS ) ; return new JobStatus ( job , elapsedNowMillis + delayMillis , JobStatus . NO_LATEST_RUNTIME , backoffAttemps ) ;


signature:
public class JobSchedulerService { /** * Called after a periodic has executed so we can to re - add it . We take the last execution time
 * of the job to be the time of completion ( i . e . the time at which this function is called ) .
 * This could be inaccurate b / c the job can run for as long as
 * { @ link com . android . server . job . JobServiceContext # EXECUTING _ TIMESLICE _ MILLIS } , but will lead
 * to underscheduling at least , rather than if we had taken the last execution time to be the
 * start of the execution .
 * @ return A new job representing the execution criteria for this instantiation of the
 * recurring job . */
private JobStatus reschedulePeriodicJob ( JobStatus job ) { } }

implementation:
final long elapsedNow = SystemClock . elapsedRealtime ( ) ; // Compute how much of the period is remaining .
long runEarly = Math . max ( job . getLatestRunTimeElapsed ( ) - elapsedNow , 0 ) ; long newEarliestRunTimeElapsed = elapsedNow + runEarly ; long period = job . getJob ( ) . getIntervalMillis ( ) ; long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period ; return new JobStatus ( job , newEarliestRunTimeElapsed , newLatestRuntimeElapsed , 0 /* backoffAttempt */
) ;


signature:
public class ArraySet { /** * Make the array map empty . All storage is released . */
@ Override public void clear ( ) { } }

implementation: if ( mSize != 0 ) { freeArrays ( mHashes , mArray , mSize ) ; mHashes = EmptyArray . INT ; mArray = EmptyArray . OBJECT ; mSize = 0 ; }


signature:
public class ArraySet { /** * Perform a { @ link # add ( Object ) } of all values in < var > array < / var >
 * @ param array The array whose contents are to be retrieved . */
public void addAll ( ArraySet < ? extends E > array ) { } }

implementation: final int N = array . mSize ; ensureCapacity ( mSize + N ) ; if ( mSize == 0 ) { if ( N > 0 ) { System . arraycopy ( array . mHashes , 0 , mHashes , 0 , N ) ; System . arraycopy ( array . mArray , 0 , mArray , 0 , N ) ; mSize = N ; } } else { for ( int i = 0 ; i < N ; i ++ ) { add ( array . valueAt ( i ) ) ; } }


signature:
public class ArraySet { /** * Remove the key / value mapping at the given index .
 * @ param index The desired index , must be between 0 and { @ link # size ( ) } - 1.
 * @ return Returns the value that was stored at this index . */
public E removeAt ( int index ) { } }

implementation:
final Object old = mArray [ index ] ; if ( mSize <= 1 ) { // Now empty .
if ( DEBUG ) Log . d ( TAG , "remove: shrink from " + mHashes . length + " to 0" ) ; freeArrays ( mHashes , mArray , mSize ) ; mHashes = EmptyArray . INT ; mArray = EmptyArray . OBJECT ; mSize = 0 ; } else { if ( mHashes . length > ( BASE_SIZE * 2 ) && mSize < mHashes . length / 3 ) { // Shrunk enough to reduce size of arrays . We don ' t allow it to
// shrink smaller than ( BASE _ SIZE * 2 ) to avoid flapping between
// that and BASE _ SIZE .
final int n = mSize > ( BASE_SIZE * 2 ) ? ( mSize + ( mSize >> 1 ) ) : ( BASE_SIZE * 2 ) ; if ( DEBUG ) Log . d ( TAG , "remove: shrink from " + mHashes . length + " to " + n ) ; final int [ ] ohashes = mHashes ; final Object [ ] oarray = mArray ; allocArrays ( n ) ; mSize -- ; if ( index > 0 ) { if ( DEBUG ) Log . d ( TAG , "remove: copy from 0-" + index + " to 0" ) ; System . arraycopy ( ohashes , 0 , mHashes , 0 , index ) ; System . arraycopy ( oarray , 0 , mArray , 0 , index ) ; } if ( index < mSize ) { if ( DEBUG ) Log . d ( TAG , "remove: copy from " + ( index + 1 ) + "-" + mSize + " to " + index ) ; System . arraycopy ( ohashes , index + 1 , mHashes , index , mSize - index ) ; System . arraycopy ( oarray , index + 1 , mArray , index , mSize - index ) ; } } else { mSize -- ; if ( index < mSize ) { if ( DEBUG ) Log . d ( TAG , "remove: move " + ( index + 1 ) + "-" + mSize + " to " + index ) ; System . arraycopy ( mHashes , index + 1 , mHashes , index , mSize - index ) ; System . arraycopy ( mArray , index + 1 , mArray , index , mSize - index ) ; } mArray [ mSize ] = null ; } } return ( E ) old ;


signature: public class ArraySet { private MapCollections < E , E > getCollection ( ) { } }

implementation: if ( mCollections == null ) { mCollections = new MapCollections < E , E > ( ) { @ Override protected int colGetSize ( ) { return mSize ; } @ Override protected Object colGetEntry ( int index , int offset ) { return mArray [ index ] ; } @ Override protected int colIndexOfKey ( Object key ) { return indexOf ( key ) ; } @ Override protected int colIndexOfValue ( Object value ) { return indexOf ( value ) ; } @ Override protected Map < E , E > colGetMap ( ) { throw new UnsupportedOperationException ( "not a map" ) ; } @ Override protected void colPut ( E key , E value ) { add ( key ) ; } @ Override protected E colSetValue ( int index , E value ) { throw new UnsupportedOperationException ( "not a map" ) ; } @ Override protected void colRemoveAt ( int index ) { removeAt ( index ) ; } @ Override protected void colClear ( ) { clear ( ) ; } } ; } return mCollections ;


signature:
public class IJobServiceCompat { /** * getCallback ( ) is a hidden method on JobParameters , so use reflection to get at it . */
private static android . app . job . IJobCallback getCallback ( android . app . job . JobParameters params ) { } }

implementation: Method method ; try { method = params . getClass ( ) . getDeclaredMethod ( "getCallback" ) ; method . setAccessible ( true ) ; return ( android . app . job . IJobCallback ) method . invoke ( params ) ; } catch ( NoSuchMethodException e ) { throw new RuntimeException ( e ) ; } catch ( InvocationTargetException e ) { throw new RuntimeException ( e ) ; } catch ( IllegalAccessException e ) { throw new RuntimeException ( e ) ; }


signature:
public class MainActivity { /** * UI onclick listener to schedule a job . What this job is is defined in
 * TestJobService # scheduleJob ( ) . */
public void scheduleJob ( View v ) { } }

implementation: if ( ! ensureTestService ( ) ) { return ; } JobInfo . Builder builder = new JobInfo . Builder ( kJobId ++ , mServiceComponent ) ; String delay = mDelayEditText . getText ( ) . toString ( ) ; if ( delay != null && ! TextUtils . isEmpty ( delay ) ) { builder . setMinimumLatency ( Long . valueOf ( delay ) * 1000 ) ; } String deadline = mDeadlineEditText . getText ( ) . toString ( ) ; if ( deadline != null && ! TextUtils . isEmpty ( deadline ) ) { builder . setOverrideDeadline ( Long . valueOf ( deadline ) * 1000 ) ; } String period = mPeriodEditText . getText ( ) . toString ( ) ; if ( period != null && ! TextUtils . isEmpty ( period ) ) { builder . setPeriodic ( Long . valueOf ( period ) * 1000 ) ; } boolean requiresUnmetered = mWiFiConnectivityRadioButton . isChecked ( ) ; boolean requiresAnyConnectivity = mAnyConnectivityRadioButton . isChecked ( ) ; if ( requiresUnmetered ) { builder . setRequiredNetworkType ( JobInfo . NETWORK_TYPE_UNMETERED ) ; } else if ( requiresAnyConnectivity ) { builder . setRequiredNetworkType ( JobInfo . NETWORK_TYPE_ANY ) ; } builder . setRequiresDeviceIdle ( mRequiresIdleCheckbox . isChecked ( ) ) ; builder . setRequiresCharging ( mRequiresChargingCheckBox . isChecked ( ) ) ; builder . setPersisted ( mPersistedCheckbox . isChecked ( ) ) ; String backoffTime = mBackoffDelayEditText . getText ( ) . toString ( ) ; if ( backoffTime != null && ! TextUtils . isEmpty ( backoffTime ) ) { int backoffPolicy = mBackoffLinearRadioButton . isChecked ( ) ? JobInfo . BACKOFF_POLICY_LINEAR : JobInfo . BACKOFF_POLICY_EXPONENTIAL ; builder . setBackoffCriteria ( Long . valueOf ( backoffTime ) * 1000 , backoffPolicy ) ; } try { mTestService . scheduleJob ( builder . build ( ) ) ; } catch ( IllegalArgumentException e ) { Toast . makeText ( this , e . getMessage ( ) , Toast . LENGTH_SHORT ) . show ( ) ; } catch ( IllegalStateException e ) { Toast . makeText ( this , e . getMessage ( ) , Toast . LENGTH_SHORT ) . show ( ) ; }


signature:
public class MainActivity { /** * Receives callback from the service when a job has landed on the app . Colours the UI and post
 * a message to uncolour it after a second . */
public void onReceivedStartJob ( JobParameters params ) { } }

implementation:
mShowStartView . setBackgroundColor ( startJobColor ) ; Message m = Message . obtain ( mHandler , MSG_UNCOLOUR_START ) ; mHandler . sendMessageDelayed ( m , 1000L ) ; // uncolour in 1 second .
mParamsTextView . setText ( "Executing: " + params . getJobId ( ) + " " + params . getExtras ( ) ) ;


signature:
public class MainActivity { /** * Receives callback from the service when a job that previously landed on the app must stop
 * executing . Colours the UI and post a message to uncolour it after a second . */
public void onReceivedStopJob ( ) { } }

implementation:
mShowStopView . setBackgroundColor ( stopJobColor ) ; Message m = Message . obtain ( mHandler , MSG_UNCOLOUR_STOP ) ; mHandler . sendMessageDelayed ( m , 2000L ) ; // uncolour in 1 second .
mParamsTextView . setText ( "" ) ;


signature:
public class TimeReceiver { /** * Determines whether this controller can stop tracking the given job .
 * The controller is no longer interested in a job once its time constraint is satisfied , and
 * the job ' s deadline is fulfilled - unlike other controllers a time constraint can ' t toggle
 * back and forth . */
private static boolean canStopTrackingJob ( JobStatus job ) { } }

implementation: return ( ! job . hasTimingDelayConstraint ( ) || job . timeDelayConstraintSatisfied . get ( ) ) && ( ! job . hasDeadlineConstraint ( ) || job . deadlineConstraintSatisfied . get ( ) ) ;


signature:
public class JobService { /** * Callback to inform the JobManager you ' ve finished executing . This can be called from any
 * thread , as it will ultimately be run on your application ' s main thread . When the system
 * receives this message it will release the wakelock being held .
 * You can specify post - execution behaviour to the scheduler here with
 * < code > needsReschedule < / code > . This will apply a back - off timer to your job based on
 * the default , or what was set with
 * { @ link android . app . job . JobInfo . Builder # setBackoffCriteria ( long , int ) } . The original
 * requirements are always honoured even for a backed - off job . Note that a job running in
 * idle mode will not be backed - off . Instead what will happen is the job will be re - added
 * to the queue and re - executed within a future idle maintenance window .
 * @ param params Parameters specifying system - provided info about this job , this was given to
 * your application in { @ link # onStartJob ( JobParameters ) } .
 * @ param needsReschedule True if this job should be rescheduled according to the back - off
 * criteria specified at schedule - time . False otherwise . */
public final void jobFinished ( JobParameters params , boolean needsReschedule ) { } }

implementation: ensureHandler ( ) ; Message m = Message . obtain ( mHandler , MSG_JOB_FINISHED , params ) ; m . arg2 = needsReschedule ? 1 : 0 ; m . sendToTarget ( ) ;


signature:
public class PersistableBundle { /** * Inserts a String value into the mapping of this Bundle , replacing any existing value for the
 * given key . Either key or value may be null .
 * @ param key a String , or null
 * @ param value a String , or null */
public void putString ( String key , String value ) { } }

implementation: PersistableBundleCompat . putString ( bundle , key , value ) ;


signature:
public class PersistableBundle { /** * Inserts a String array value into the mapping of this Bundle , replacing any existing value
 * for the given key . Either key or value may be null .
 * @ param key a String , or null
 * @ param value a String array object , or null */
public void putStringArray ( String key , String [ ] value ) { } }

implementation: PersistableBundleCompat . putStringArray ( bundle , key , value ) ;


signature:
public class PersistableBundle { /** * Returns the value associated with the given key , or defaultValue if no mapping of the desired
 * type exists for the given key or if a null value is explicitly associated with the given
 * key .
 * @ param key a String , or null
 * @ param defaultValue Value to return if key does not exist or if a null value is associated
 * with the given key .
 * @ return the String value associated with the given key , or defaultValue if no valid String
 * object is currently mapped to that key . */
public String getString ( String key , String defaultValue ) { } }

implementation: return PersistableBundleCompat . getString ( bundle , key , defaultValue ) ;


signature:
public class PersistableBundle { /** * Inserts a PersistableBundle value into the mapping of this Bundle , replacing any existing
 * value for the given key . Either key or value may be null .
 * @ param key a String , or null
 * @ param value a Bundle object , or null */
public void putPersistableBundle ( String key , PersistableBundle value ) { } }

implementation: PersistableBundleCompat . putPersistableBundle ( bundle , key , value . bundle ) ;


signature:
public class JobStatus { /** * Dumpsys infrastructure */
public void dump ( PrintWriter pw , String prefix ) { } }

implementation: pw . print ( prefix ) ; pw . println ( this . toString ( ) ) ;


signature:
public class JobScheduler { /** * Get an instance of a the { @ link JobScheduler } , which will delegate to the android one in api
 * 21 + and use a backported version on older apis .
 * @ param context the context
 * @ return a JobScheduler instance */
public static JobScheduler getInstance ( Context context ) { } }

implementation: if ( Build . VERSION . SDK_INT >= Build . VERSION_CODES . LOLLIPOP ) { return JobSchedulerLollipopDelegate . getLollipopInstance ( context ) ; } else { return JobSchedulerCompat . getCompatInstance ( context ) ; }


signature:
public class PowerReceiver { /** * private static final long STABLE _ CHARGING _ THRESHOLD _ MILLIS = 1000 ; / / 1 second . */
@ Override public void onReceive ( Context context , Intent intent ) { } }

implementation:
final String action = intent . getAction ( ) ; if ( Intent . ACTION_BATTERY_LOW . equals ( action ) ) { // If we get this action , the battery is discharging = > it isn ' t plugged in so
// there ' s no work to cancel . We track this variable for the case where it is
// charging , but hasn ' t been for long enough to be healthy .
ControllerPrefs . getInstance ( context ) . edit ( ) . setBatteryLow ( true ) . apply ( ) ; } else if ( Intent . ACTION_BATTERY_OKAY . equals ( action ) ) { ControllerPrefs . getInstance ( context ) . edit ( ) . setBatteryLow ( false ) . apply ( ) ; maybeReportNewChargingState ( context , isCharging ( context ) ) ; } else if ( Intent . ACTION_POWER_CONNECTED . equals ( action ) ) { // Set up an alarm for ACTION _ CHARGING _ STABLE - we don ' t want to kick off tasks
// here if the user unplugs the phone immediately .
setStableChargingAlarm ( context ) ; } else if ( Intent . ACTION_POWER_DISCONNECTED . equals ( action ) ) { // If an alarm is set , breathe a sigh of relief and cancel it - crisis averted .
cancelStableChargingAlarm ( context ) ; maybeReportNewChargingState ( context , false ) ; } else if ( ACTION_CHARGING_STABLE . equals ( action ) ) { // Here ' s where we actually do the notify for a task being ready .
maybeReportNewChargingState ( context , ! isBatteryLow ( context ) ) ; }


signature:
public class SnackContainer { /** * Restoration * */
public void restoreState ( Bundle state , View v ) { } }

implementation: Parcelable [ ] messages = state . getParcelableArray ( SAVED_MSGS ) ; boolean showImmediately = true ; for ( Parcelable message : messages ) { showSnack ( ( Snack ) message , v , null , showImmediately ) ; showImmediately = false ; }


signature:
public class SnackContainer { /** * Helpers */
private int getPxFromDp ( int dp ) { } }

implementation: Resources rs = getResources ( ) ; int pxConverter = ( int ) TypedValue . applyDimension ( TypedValue . COMPLEX_UNIT_DIP , 1 , rs . getDisplayMetrics ( ) ) ; int px = pxConverter * dp ; return px ;


signature:
public class Snack { /** * writes data to parcel */
public void writeToParcel ( Parcel out , int flags ) { } }

implementation: out . writeString ( mMessage ) ; out . writeString ( mActionMessage ) ; out . writeInt ( mActionIcon ) ; out . writeParcelable ( mToken , 0 ) ; out . writeInt ( ( int ) mDuration ) ; out . writeParcelable ( mBtnTextColor , 0 ) ; out . writeParcelable ( mBackgroundColor , 0 ) ; out . writeInt ( mHeight ) ; out . writeValue ( mTypeface ) ;


signature:
public class SnackBar { /** * Calculates the height of the SnackBar
 * @ return the height of the SnackBar */
public int getHeight ( ) { } }

implementation: mParentView . measure ( View . MeasureSpec . makeMeasureSpec ( mParentView . getWidth ( ) , View . MeasureSpec . EXACTLY ) , View . MeasureSpec . makeMeasureSpec ( mParentView . getHeight ( ) , View . MeasureSpec . AT_MOST ) ) ; return mParentView . getMeasuredHeight ( ) ;


signature:
public class BasicLTIUtil { /** * Simple Debug Print Mechanism */
public static void dPrint ( String str ) { } }

implementation: if ( verbosePrint ) { System . out . println ( str ) ; } M_log . fine ( str ) ;


signature:
public class BasicLTIUtil { /** * Any properties which are not well known ( i . e . in
 * { @ link BasicLTIConstants # validPropertyNames } ) will be mapped to custom
 * properties per the specified semantics . NOTE : no blacklisting of keys is
 * performed .
 * @ param rawProperties A set of properties that will be cleaned .
 * @ return A cleansed version of rawProperties . */
public static Map < String , String > cleanupProperties ( final Map < String , String > rawProperties ) { } }

implementation: return cleanupProperties ( rawProperties , null ) ;


signature:
public class BasicLTIUtil { /** * Any properties which are not well known ( i . e . in
 * { @ link BasicLTIConstants # validPropertyNames } ) will be mapped to custom
 * properties per the specified semantics .
 * @ param rawProperties A set of properties that will be cleaned .
 * @ param blackList An array of { @ link String } s which are considered unsafe
 * to be included in launch data . Any matches will be removed from the
 * return .
 * @ return A cleansed version of rawProperties . */
public static Map < String , String > cleanupProperties ( final Map < String , String > rawProperties , final String [ ] blackList ) { } }

implementation:
final Map < String , String > newProp = new HashMap < String , String > ( rawProperties . size ( ) ) ; // roughly the same size
for ( String okey : rawProperties . keySet ( ) ) { final String key = okey . trim ( ) ; if ( blackList != null ) { boolean blackListed = false ; for ( String blackKey : blackList ) { if ( blackKey . equals ( key ) ) { blackListed = true ; break ; } } if ( blackListed ) { continue ; } } final String value = rawProperties . get ( key ) ; if ( value == null || "" . equals ( value ) ) { // remove null or empty values
continue ; } if ( isSpecifiedPropertyName ( key ) ) { // a well known property name
newProp . put ( key , value ) ; } else { // convert to a custom property name
newProp . put ( adaptToCustomPropertyName ( key ) , value ) ; } } return newProp ;


signature:
public class BasicLTIUtil { /** * Any properties which are not well known ( i . e . in
 * { @ link BasicLTIConstants # validPropertyNames } ) will be mapped to custom
 * properties per the specified semantics .
 * @ deprecated See { @ link # cleanupProperties ( Map ) }
 * @ param rawProperties A set of { @ link Properties } that will be cleaned .
 * Keys must be of type { @ link String } .
 * @ return A cleansed version of { @ link Properties } . */
public static Properties cleanupProperties ( final Properties rawProperties ) { } }

implementation: final Map < String , String > map = cleanupProperties ( convertToMap ( rawProperties ) , null ) ; return convertToProperties ( map ) ;


signature:
public class BasicLTIUtil { /** * Checks to see if the passed propertyName is equal to one of the Strings
 * contained in { @ link BasicLTIConstants # validPropertyNames } . String
 * matching is case sensitive .
 * @ param propertyName
 * @ return true if propertyName is equal to one of the Strings contained in
 * { @ link BasicLTIConstants # validPropertyNames } or is a custom parameter oe
 * extension parameter ; else return false . */
public static boolean isSpecifiedPropertyName ( final String propertyName ) { } }

implementation: boolean found = false ; if ( propertyName . startsWith ( CUSTOM_PREFIX ) ) { return true ; } if ( propertyName . startsWith ( EXTENSION_PREFIX ) ) { return true ; } if ( propertyName . startsWith ( OAUTH_PREFIX ) ) { return true ; } for ( String key : BasicLTIConstants . validPropertyNames ) { if ( key . equals ( propertyName ) ) { found = true ; break ; } } return found ;


signature:
public class BasicLTIUtil { /** * A simple utility method which implements the specified semantics of
 * custom properties .
 * i . e . The parameter names are mapped to lower case and any character that
 * is neither a number nor letter in a parameter name is replaced with an
 * " underscore " .
 * e . g . Review : Chapter = 1.2.56 would map to custom _ review _ chapter = 1.2.56.
 * @ param propertyName
 * @ return */
public static String adaptToCustomPropertyName ( final String propertyName ) { } }

implementation: if ( propertyName == null || "" . equals ( propertyName ) ) { throw new IllegalArgumentException ( "propertyName cannot be null" ) ; } String customName = propertyName . toLowerCase ( ) ; customName = CUSTOM_REGEX . matcher ( customName ) . replaceAll ( UNDERSCORE ) ; if ( ! customName . startsWith ( CUSTOM_PREFIX ) ) { customName = CUSTOM_PREFIX + customName ; } return customName ;


signature:
public class BasicLTIUtil { /** * Add the necessary fields and sign .
 * @ deprecated See :
 * { @ link BasicLTIUtil # signProperties ( Map , String , String , String , String , String , String , String , String , String ) }
 * @ param postProp
 * @ param url
 * @ param method
 * @ param oauth _ consumer _ key
 * @ param oauth _ consumer _ secret
 * @ param org _ id See : { @ link BasicLTIConstants # TOOL _ CONSUMER _ INSTANCE _ GUID }
 * @ param org _ desc See :
 * { @ link BasicLTIConstants # TOOL _ CONSUMER _ INSTANCE _ DESCRIPTION }
 * @ param org _ url See : { @ link BasicLTIConstants # TOOL _ CONSUMER _ INSTANCE _ URL }
 * @ return */
public static Properties signProperties ( Properties postProp , String url , String method , String oauth_consumer_key , String oauth_consumer_secret , String org_id , String org_desc , String org_url ) { } }

implementation: final Map < String , String > signedMap = signProperties ( convertToMap ( postProp ) , url , method , oauth_consumer_key , oauth_consumer_secret , org_id , org_desc , org_url , null , null ) ; return convertToProperties ( signedMap ) ;


signature:
public class BasicLTIUtil { /** * Add the necessary fields and sign .
 * @ param postProp
 * @ param url
 * @ param method
 * @ param oauth _ consumer _ key
 * @ param oauth _ consumer _ secret
 * @ param tool _ consumer _ instance _ guid See :
 * { @ link BasicLTIConstants # TOOL _ CONSUMER _ INSTANCE _ GUID }
 * @ param tool _ consumer _ instance _ description See :
 * { @ link BasicLTIConstants # TOOL _ CONSUMER _ INSTANCE _ DESCRIPTION }
 * @ param tool _ consumer _ instance _ url See :
 * { @ link BasicLTIConstants # TOOL _ CONSUMER _ INSTANCE _ URL }
 * @ param tool _ consumer _ instance _ name See :
 * { @ link BasicLTIConstants # TOOL _ CONSUMER _ INSTANCE _ NAME }
 * @ param tool _ consumer _ instance _ contact _ email See :
 * { @ link BasicLTIConstants # TOOL _ CONSUMER _ INSTANCE _ CONTACT _ EMAIL }
 * @ return */
public static Map < String , String > signProperties ( Map < String , String > postProp , String url , String method , String oauth_consumer_key , String oauth_consumer_secret , String tool_consumer_instance_guid , String tool_consumer_instance_description , String tool_consumer_instance_url , String tool_consumer_instance_name , String tool_consumer_instance_contact_email ) { } }

implementation:
postProp = BasicLTIUtil . cleanupProperties ( postProp ) ; if ( postProp . get ( LTI_VERSION ) == null ) { postProp . put ( LTI_VERSION , "LTI-1p0" ) ; } if ( postProp . get ( LTI_MESSAGE_TYPE ) == null ) { postProp . put ( LTI_MESSAGE_TYPE , "basic-lti-launch-request" ) ; } // Allow caller to internationalize this for us . . .
if ( postProp . get ( BASICLTI_SUBMIT ) == null ) { postProp . put ( BASICLTI_SUBMIT , "Launch Endpoint with BasicLTI Data" ) ; } if ( tool_consumer_instance_guid != null ) { postProp . put ( TOOL_CONSUMER_INSTANCE_GUID , tool_consumer_instance_guid ) ; } if ( tool_consumer_instance_description != null ) { postProp . put ( TOOL_CONSUMER_INSTANCE_DESCRIPTION , tool_consumer_instance_description ) ; } if ( tool_consumer_instance_url != null ) { postProp . put ( TOOL_CONSUMER_INSTANCE_URL , tool_consumer_instance_url ) ; } if ( tool_consumer_instance_name != null ) { postProp . put ( TOOL_CONSUMER_INSTANCE_NAME , tool_consumer_instance_name ) ; } if ( tool_consumer_instance_contact_email != null ) { postProp . put ( TOOL_CONSUMER_INSTANCE_CONTACT_EMAIL , tool_consumer_instance_contact_email ) ; } if ( postProp . get ( "oauth_callback" ) == null ) { postProp . put ( "oauth_callback" , "about:blank" ) ; } if ( oauth_consumer_key == null || oauth_consumer_secret == null ) { dPrint ( "No signature generated in signProperties" ) ; return postProp ; } OAuthMessage oam = new OAuthMessage ( method , url , postProp . entrySet ( ) ) ; OAuthConsumer cons = new OAuthConsumer ( "about:blank" , oauth_consumer_key , oauth_consumer_secret , null ) ; OAuthAccessor acc = new OAuthAccessor ( cons ) ; try { oam . addRequiredParameters ( acc ) ; // System . out . println ( " Base Message String \ n " + OAuthSignatureMethod . getBaseString ( oam ) + " \ n " ) ;
List < Map . Entry < String , String > > params = oam . getParameters ( ) ; Map < String , String > nextProp = new HashMap < String , String > ( ) ; // Convert to Map < String , String >
for ( final Map . Entry < String , String > entry : params ) { nextProp . put ( entry . getKey ( ) , entry . getValue ( ) ) ; } return nextProp ; } catch ( net . oauth . OAuthException e ) { M_log . warning ( "BasicLTIUtil.signProperties OAuth Exception " + e . getMessage ( ) ) ; throw new Error ( e ) ; } catch ( java . io . IOException e ) { M_log . warning ( "BasicLTIUtil.signProperties IO Exception " + e . getMessage ( ) ) ; throw new Error ( e ) ; } catch ( java . net . URISyntaxException e ) { M_log . warning ( "BasicLTIUtil.signProperties URI Syntax Exception " + e . getMessage ( ) ) ; throw new Error ( e ) ; }


signature:
public class BasicLTIUtil { /** * Check if the properties are properly signed
 * @ deprecated See :
 * { @ link BasicLTIUtil # checkProperties ( Map , String , String , String , String ) }
 * @ param postProp
 * @ param url
 * @ param method
 * @ param oauth _ consumer _ key
 * @ param oauth _ consumer _ secret
 * @ return */
public static boolean checkProperties ( Properties postProp , String url , String method , String oauth_consumer_key , String oauth_consumer_secret ) { } }

implementation: return checkProperties ( convertToMap ( postProp ) , url , method , oauth_consumer_key , oauth_consumer_secret ) ;