Input: Create a { @link HollowDataAccess } for the prior state of the supplied { @link HollowReadStateEngine } after a delta has been applied . [CODESPLIT] public HollowHistoricalStateDataAccess createBasedOnNewDelta ( long version , HollowReadStateEngine stateEngine ) { IntMapOrdinalRemapper typeRemovedOrdinalMapping = new IntMapOrdinalRemapper ( ) ; List < HollowTypeReadState > historicalTypeStates = new ArrayList < HollowTypeReadState > ( stateEngine . getTypeStates ( ) . size ( ) ) ; for ( HollowTypeReadState typeState : stateEngine . getTypeStates ( ) ) { createDeltaHistoricalTypeState ( typeRemovedOrdinalMapping , historicalTypeStates , typeState ) ; } HollowHistoricalStateDataAccess dataAccess = new HollowHistoricalStateDataAccess ( totalHistory , version , stateEngine , historicalTypeStates , typeRemovedOrdinalMapping , Collections . < String , HollowHistoricalSchemaChange > emptyMap ( ) ) ; dataAccess . setNextState ( stateEngine ) ; return dataAccess ; }


Input: Create a { @link HollowDataAccess } for a { @link HollowHistory } . Remap ordinal spaces for all prior historical versions in the { @link HollowHistory } for consistency . [CODESPLIT] public HollowHistoricalStateDataAccess createConsistentOrdinalHistoricalStateFromDoubleSnapshot ( long version , HollowReadStateEngine previous ) { return new HollowHistoricalStateDataAccess ( totalHistory , version , previous , IdentityOrdinalRemapper . INSTANCE , Collections . < String , HollowHistoricalSchemaChange > emptyMap ( ) ) ; }


Input: Create a { @link HollowDataAccess } for a historical state after a double snapshot occurs without a { @link HollowHistory } . [CODESPLIT] public HollowHistoricalStateDataAccess createHistoricalStateFromDoubleSnapshot ( long version , HollowReadStateEngine previous , HollowReadStateEngine current , DiffEqualityMappingOrdinalRemapper ordinalRemapper ) { HollowWriteStateEngine writeEngine = HollowWriteStateCreator . createWithSchemas ( schemasWithoutKeys ( previous . getSchemas ( ) ) ) ; IntMapOrdinalRemapper typeRemovedOrdinalLookupMaps = new IntMapOrdinalRemapper ( ) ; for ( HollowSchema previousSchema : HollowSchemaSorter . dependencyOrderedSchemaList ( previous ) ) { HollowTypeReadState previousTypeState = previous . getTypeState ( previousSchema . getName ( ) ) ; String typeName = previousTypeState . getSchema ( ) . getName ( ) ; IntMap ordinalLookupMap ; if ( current . getTypeState ( typeName ) == null ) { ordinalLookupMap = copyAllRecords ( previousTypeState , ordinalRemapper , writeEngine ) ; } else { HollowTypeReadState currentTypeState = current . getTypeState ( typeName ) ; BitSet currentlyPopulatedOrdinals = currentTypeState . getListener ( PopulatedOrdinalListener . class ) . getPopulatedOrdinals ( ) ; ordinalLookupMap = copyUnmatchedRecords ( previousTypeState , ordinalRemapper , currentlyPopulatedOrdinals , writeEngine ) ; } typeRemovedOrdinalLookupMaps . addOrdinalRemapping ( typeName , ordinalLookupMap ) ; } Map < String , HollowHistoricalSchemaChange > schemaChanges = calculateSchemaChanges ( previous , current , ordinalRemapper . getDiffEqualityMapping ( ) ) ; return new HollowHistoricalStateDataAccess ( totalHistory , version , roundTripStateEngine ( writeEngine ) , typeRemovedOrdinalLookupMaps , schemaChanges ) ; }


Input: Initializes the data model and restores from existing state . [CODESPLIT] public void restoreFromLastState ( ) { producer . initializeDataModel ( dataModel ) ; long latestAnnouncedVersion = announcementWatcher . getLatestVersion ( ) ; if ( latestAnnouncedVersion == HollowFilesystemAnnouncementWatcher . NO_ANNOUNCEMENT_AVAILABLE || latestAnnouncedVersion < 0 ) { return ; } restore ( latestAnnouncedVersion , blobRetriever ) ; }


Input: Runs a Hollow Cycle if successful cleans the mutations map . [CODESPLIT] public long runCycle ( ) { long recordsRemoved = countRecordsToRemove ( ) ; long recordsAddedOrModified = this . mutations . values ( ) . size ( ) - recordsRemoved ; try { long version = producer . runCycle ( populator ) ; if ( version == lastSucessfulCycle ) { return version ; } listeners . fireIncrementalCycleComplete ( version , recordsAddedOrModified , recordsRemoved , new HashMap < String , Object > ( cycleMetadata ) ) ; //Only clean changes when the version is new. clearChanges ( ) ; lastSucessfulCycle = version ; return version ; } catch ( Exception e ) { listeners . fireIncrementalCycleFail ( e , recordsAddedOrModified , recordsRemoved , new HashMap < String , Object > ( cycleMetadata ) ) ; return FAILED_VERSION ; } finally { clearCycleMetadata ( ) ; } }


Input: Parallel execution . Modifies the mutation ConcurrentHashMap in parallel based on a Callback . <p > Note : This could be replaced with Java 8 parallelStream and lambadas instead of Callback interface < / p > [CODESPLIT] private void executeInParallel ( Collection < Object > objList , String description , final Callback callback ) { SimultaneousExecutor executor = new SimultaneousExecutor ( threadsPerCpu , getClass ( ) , description ) ; for ( final Object obj : objList ) { executor . execute ( ( ) -> callback . call ( obj ) ) ; } try { executor . awaitSuccessfulCompletion ( ) ; } catch ( Exception e ) { throw new RuntimeException ( e ) ; } }


Input: Exclude the record which matches the specified key . [CODESPLIT] public void excludeKey ( HollowPrimaryKeyIndex idx , Object ... key ) { int excludeOrdinal = idx . getMatchingOrdinal ( key ) ; if ( excludeOrdinal >= 0 ) { BitSet excludedOrdinals = this . excludedOrdinals . get ( idx . getTypeState ( ) ) ; if ( excludedOrdinals == null ) { excludedOrdinals = new BitSet ( idx . getTypeState ( ) . maxOrdinal ( ) + 1 ) ; this . excludedOrdinals . put ( idx . getTypeState ( ) , excludedOrdinals ) ; } excludedOrdinals . set ( excludeOrdinal ) ; } }


Input: Exclude any objects which are referenced by excluded objects . [CODESPLIT] public void excludeReferencedObjects ( ) { Set < HollowReadStateEngine > stateEngines = new HashSet < HollowReadStateEngine > ( ) ; for ( Map . Entry < HollowTypeReadState , BitSet > entry : excludedOrdinals . entrySet ( ) ) stateEngines . ( entry . getKey ( ) . getStateEngine ( ) ) ; for ( HollowReadStateEngine stateEngine : stateEngines ) { Map < String , BitSet > typeBitSetsForStateEngine = new HashMap < String , BitSet > ( ) ; for ( Map . Entry < HollowTypeReadState , BitSet > entry : excludedOrdinals . entrySet ( ) ) { if ( entry . getKey ( ) . getStateEngine ( ) == stateEngine ) { String type = entry . getKey ( ) . getSchema ( ) . getName ( ) ; typeBitSetsForStateEngine . put ( type , BitSet . valueOf ( entry . getValue ( ) . toLongArray ( ) ) ) ; } } TransitiveSetTraverser . addTransitiveMatches ( stateEngine , typeBitSetsForStateEngine ) ; for ( Map . Entry < String , BitSet > entry : typeBitSetsForStateEngine . entrySet ( ) ) excludedOrdinals . put ( stateEngine . getTypeState ( entry . getKey ( ) ) , entry . getValue ( ) ) ; } }


Input: Write the header to the data output stream [CODESPLIT] public void writeHeader ( HollowBlobHeader header , DataOutputStream dos ) throws IOException { /// save 4 bytes to indicate FastBlob version header.  This will be changed to indicate backwards incompatibility. dos . writeInt ( HollowBlobHeader . HOLLOW_BLOB_VERSION_HEADER ) ; /// Write randomized tag data -- every state gets a random 64-bit tag. /// When attempting to apply a delta, the originating state's random 64-bit tag is compared against the current 64-bit tag. /// This prevents deltas from being applied to incorrect states. dos . writeLong ( header . getOriginRandomizedTag ( ) ) ; dos . writeLong ( header . getDestinationRandomizedTag ( ) ) ; /// write the schemas contained in this blob to the stream in the pre v2.2.0 backwards compatibility envelope ByteArrayOutputStream schemasStream = new ByteArrayOutputStream ( ) ; VarInt . writeVInt ( schemasStream , header . getSchemas ( ) . size ( ) ) ; for ( HollowSchema schema : header . getSchemas ( ) ) schema . writeTo ( schemasStream ) ; byte [ ] schemasData = schemasStream . toByteArray ( ) ; VarInt . writeVInt ( dos , schemasData . length + 1 ) ; // plus one byte for new backwards compatibility envelope. dos . write ( schemasData ) ; ///backwards compatibility -- new data can be added here by first indicating number of bytes used, will be skipped by existing readers. VarInt . writeVInt ( dos , 0 ) ; /// write the header tags -- intended to include input source data versions dos . writeShort ( header . getHeaderTags ( ) . size ( ) ) ; for ( Map . Entry < String , String > headerTag : header . getHeaderTags ( ) . entrySet ( ) ) { dos . writeUTF ( headerTag . getKey ( ) ) ; dos . writeUTF ( headerTag . getValue ( ) ) ; } }


Input: Warning : Not thread - safe . Should only be called within the update thread . [CODESPLIT] public int bitsRequiredForField ( String fieldName ) { int fieldIndex = schema . getPosition ( fieldName ) ; return fieldIndex == - 1 ? 0 : currentDataVolatile . bitsPerField [ fieldIndex ] ; }


Input: Calculates the memory heap footprint and populated ordinals per type and total [CODESPLIT] void calculateTypeMetrics ( HollowReadStateEngine hollowReadStateEngine ) { Collection < HollowTypeReadState > typeStates = hollowReadStateEngine . getTypeStates ( ) ; if ( typeStates == null ) return ; totalHeapFootprint = 0L ; totalPopulatedOrdinals = 0 ; for ( HollowTypeReadState typeState : typeStates ) { long heapCost = typeState . getApproximateHeapFootprintInBytes ( ) ; totalHeapFootprint += heapCost ; int populatedOrdinals = typeState . getPopulatedOrdinals ( ) . cardinality ( ) ; totalPopulatedOrdinals += populatedOrdinals ; String type = typeState . getSchema ( ) . getName ( ) ; typeHeapFootprint . put ( type , heapCost ) ; typePopulatedOrdinals . put ( type , populatedOrdinals ) ; } }


Input: When provided a set of { @link PrimaryKey } will ensure that no duplicate records are added to the destination state . [CODESPLIT] public void setPrimaryKeys ( PrimaryKey ... newKeys ) { Objects . requireNonNull ( newKeys ) ; if ( newKeys . length == 0 ) { return ; } if ( inputs . length == 1 ) { return ; } /// deduplicate new keys with existing keys //process existing ones first Map < String , PrimaryKey > keysByType = new HashMap <> ( ) ; for ( PrimaryKey primaryKey : primaryKeys ) { keysByType . put ( primaryKey . getType ( ) , primaryKey ) ; } // allow override for ( PrimaryKey primaryKey : newKeys ) { keysByType . put ( primaryKey . getType ( ) , primaryKey ) ; } this . primaryKeys = sortPrimaryKeys ( new ArrayList <> ( keysByType . values ( ) ) ) ; }


Input: Perform the combine operation . [CODESPLIT] public void combine ( ) { SimultaneousExecutor executor = new SimultaneousExecutor ( getClass ( ) , "combine" ) ; final int numThreads = executor . getCorePoolSize ( ) ; createOrdinalRemappers ( ) ; createHashOrderIndependentOrdinalMaps ( ) ; final Set < String > processedTypes = new HashSet <> ( ) ; final Set < PrimaryKey > processedPrimaryKeys = new HashSet <> ( ) ; final Set < PrimaryKey > selectedPrimaryKeys = new HashSet <> ( ) ; while ( processedTypes . size ( ) < output . getOrderedTypeStates ( ) . size ( ) ) { /// find the next primary keys for ( PrimaryKey key : primaryKeys ) { if ( ! processedPrimaryKeys . contains ( key ) && ! ignoredTypes . contains ( key . getType ( ) ) ) { if ( ! isAnySelectedPrimaryKeyADependencyOf ( key . getType ( ) , selectedPrimaryKeys ) ) { selectedPrimaryKeys . add ( key ) ; } } } final Set < String > typesToProcessThisIteration = new HashSet <> ( ) ; final Map < String , HollowPrimaryKeyIndex [ ] > primaryKeyIndexes = new HashMap <> ( ) ; final HollowCombinerExcludePrimaryKeysCopyDirector primaryKeyCopyDirector = new HollowCombinerExcludePrimaryKeysCopyDirector ( copyDirector ) ; for ( HollowSchema schema : output . getSchemas ( ) ) { if ( ! processedTypes . contains ( schema . getName ( ) ) && ! ignoredTypes . contains ( schema . getName ( ) ) ) { if ( selectedPrimaryKeys . isEmpty ( ) || isAnySelectedPrimaryKeyDependentOn ( schema . getName ( ) , selectedPrimaryKeys ) ) { for ( PrimaryKey pk : selectedPrimaryKeys ) { if ( pk . getType ( ) . equals ( schema . getName ( ) ) ) { HollowPrimaryKeyIndex [ ] indexes = new HollowPrimaryKeyIndex [ inputs . length ] ; for ( int i = 0 ; i < indexes . length ; i ++ ) { if ( inputs [ i ] . getTypeState ( pk . getType ( ) ) != null ) indexes [ i ] = new HollowPrimaryKeyIndex ( inputs [ i ] , pk ) ; } for ( int i = 0 ; i < indexes . length ; i ++ ) { HollowTypeReadState typeState = inputs [ i ] . getTypeState ( pk . getType ( ) ) ; if ( typeState != null ) { BitSet populatedOrdinals = typeState . getListener ( PopulatedOrdinalListener . class ) . getPopulatedOrdinals ( ) ; int ordinal = populatedOrdinals . nextSetBit ( 0 ) ; while ( ordinal != - 1 ) { if ( primaryKeyCopyDirector . shouldCopy ( typeState , ordinal ) ) { Object [ ] recordKey = indexes [ i ] . getRecordKey ( ordinal ) ; for ( int j = i + 1 ; j < indexes . length ; j ++ ) { primaryKeyCopyDirector . excludeKey ( indexes [ j ] , recordKey ) ; } } ordinal = populatedOrdinals . nextSetBit ( ordinal + 1 ) ; } } } primaryKeyIndexes . put ( pk . getType ( ) , indexes ) ; } } typesToProcessThisIteration . add ( schema . getName ( ) ) ; } } } if ( typesToProcessThisIteration . isEmpty ( ) ) break ; for ( int i = 0 ; i < numThreads ; i ++ ) { final int threadNumber = i ; executor . execute ( ( ) -> { for ( int i1 = 0 ; i1 < inputs . length ; i1 ++ ) { HollowCombinerCopyDirector copyDirector = selectedPrimaryKeys . isEmpty ( ) ? HollowCombiner . this . copyDirector : primaryKeyCopyDirector ; HollowReadStateEngine inputEngine = inputs [ i1 ] ; OrdinalRemapper ordinalRemapper = selectedPrimaryKeys . isEmpty ( ) ? ordinalRemappers [ i1 ] : new HollowCombinerPrimaryKeyOrdinalRemapper ( ordinalRemappers , primaryKeyIndexes , i1 ) ; Map < String , HollowCombinerCopier > copierMap = new HashMap <> ( ) ; List < HollowCombinerCopier > copierList = new ArrayList <> ( ) ; for ( String typeName : typesToProcessThisIteration ) { HollowTypeReadState readState = inputEngine . getTypeState ( typeName ) ; HollowTypeWriteState writeState = output . getTypeState ( typeName ) ; if ( readState != null && writeState != null ) { HollowCombinerCopier copier = new HollowCombinerCopier ( readState , writeState , ordinalRemapper ) ; copierList . add ( copier ) ; copierMap . put ( typeName , copier ) ; } } for ( String typeName : processedTypes ) { HollowTypeReadState readState = inputEngine . getTypeState ( typeName ) ; HollowTypeWriteState writeState = output . getTypeState ( typeName ) ; if ( readState != null && writeState != null ) { HollowCombinerCopier copier = new HollowCombinerCopier ( readState , writeState , ordinalRemappers [ i1 ] ) ; copierMap . put ( typeName , copier ) ; } } copiersPerType . set ( copierMap ) ; int currentOrdinal = threadNumber ; while ( ! copierList . isEmpty ( ) ) { copyOrdinalForAllStates ( currentOrdinal , copierList , ordinalRemapper , copyDirector ) ; currentOrdinal += numThreads ; } } } ) ; } try { executor . awaitSuccessfulCompletionOfCurrentTasks ( ) ; } catch ( Throwable th ) { throw new RuntimeException ( th ) ; } processedTypes . addAll ( typesToProcessThisIteration ) ; processedPrimaryKeys . addAll ( selectedPrimaryKeys ) ; selectedPrimaryKeys . clear ( ) ; } executor . shutdown ( ) ; }


Input: Concatenates all fields in order to the ByteDataBuffer supplied . This concatenation is the verbatim serialized representation in the FastBlob . [CODESPLIT] public void writeDataTo ( ByteDataBuffer buf ) { for ( int i = 0 ; i < fieldData . length ; i ++ ) { writeField ( buf , i ) ; } }


Input: Returns the buffer which should be used to serialize the data for the field at the given position in the schema . <p > [CODESPLIT] private ByteDataBuffer getFieldBuffer ( int fieldPosition ) { isNonNull [ fieldPosition ] = true ; fieldData [ fieldPosition ] . reset ( ) ; return fieldData [ fieldPosition ] ; }


Input: Write 4 consecutive bytes [CODESPLIT] private static void writeFixedLengthInt ( ByteDataBuffer fieldBuffer , int intBits ) { fieldBuffer . write ( ( byte ) ( intBits >>> 24 ) ) ; fieldBuffer . write ( ( byte ) ( intBits >>> 16 ) ) ; fieldBuffer . write ( ( byte ) ( intBits >>> 8 ) ) ; fieldBuffer . write ( ( byte ) ( intBits ) ) ; }


Input: Write 8 consecutive bytes [CODESPLIT] private static void writeFixedLengthLong ( ByteDataBuffer fieldBuffer , long intBits ) { fieldBuffer . write ( ( byte ) ( intBits >>> 56 ) ) ; fieldBuffer . write ( ( byte ) ( intBits >>> 48 ) ) ; fieldBuffer . write ( ( byte ) ( intBits >>> 40 ) ) ; fieldBuffer . write ( ( byte ) ( intBits >>> 32 ) ) ; fieldBuffer . write ( ( byte ) ( intBits >>> 24 ) ) ; fieldBuffer . write ( ( byte ) ( intBits >>> 16 ) ) ; fieldBuffer . write ( ( byte ) ( intBits >>> 8 ) ) ; fieldBuffer . write ( ( byte ) ( intBits ) ) ; }


Input: Returns a stream of matching ordinals . <p > The ordinals may be used with a generated API or the Generic Object API to inspect the matched records . [CODESPLIT] public IntStream stream ( ) { Spliterator . OfInt si = new Spliterator . OfInt ( ) { final long endBucket = selectTableStartPointer + selectTableBuckets ; long currentBucket = selectTableStartPointer ; @ Override public OfInt trySplit ( ) { // @@@ Supporting splitting and therefore enable parallelism return null ; } @ Override public boolean tryAdvance ( IntConsumer action ) { while ( currentBucket < endBucket ) { int selectOrdinal = ( int ) hashIndexState . getSelectHashArray ( ) . getElementValue ( ( currentBucket ++ ) * hashIndexState . getBitsPerSelectHashEntry ( ) , hashIndexState . getBitsPerSelectHashEntry ( ) ) - 1 ; if ( selectOrdinal != - 1 ) { action . accept ( selectOrdinal ) ; return true ; } } return false ; } @ Override public long estimateSize ( ) { // @@@ return 0 ; } @ Override public int characteristics ( ) { // @@@ ordinals are distinct? return 0 ; } } ; return StreamSupport . intStream ( si , false ) ; }


Input: Return an ordinal to the pool after the object to which it was assigned is discarded . [CODESPLIT] public void returnOrdinalToPool ( int ordinal ) { if ( size == freeOrdinals . length ) { freeOrdinals = Arrays . copyOf ( freeOrdinals , freeOrdinals . length * 3 / 2 ) ; } freeOrdinals [ size ] = ordinal ; size ++ ; }


Input: Ensure that all future ordinals are returned in ascending order . [CODESPLIT] public void sort ( ) { Arrays . sort ( freeOrdinals , 0 , size ) ; /// reverse the ordering int midpoint = size / 2 ; for ( int i = 0 ; i < midpoint ; i ++ ) { int temp = freeOrdinals [ i ] ; freeOrdinals [ i ] = freeOrdinals [ size - i - 1 ] ; freeOrdinals [ size - i - 1 ] = temp ; } }


Input: Hash a key [CODESPLIT] public static int hash ( Object key [ ] , FieldType fieldType [ ] ) { int hash = 0 ; for ( int i = 0 ; i < key . length ; i ++ ) { hash *= 31 ; hash ^= hash ( key [ i ] , fieldType [ i ] ) ; } return hash ; }


Input: Hash a single key field [CODESPLIT] public static int hash ( Object key , FieldType fieldType ) { switch ( fieldType ) { case INT : return HashCodes . hashInt ( ( ( Integer ) key ) . intValue ( ) ) ; case LONG : long longVal = ( ( Long ) key ) . longValue ( ) ; return HashCodes . hashInt ( ( int ) ( longVal ^ ( longVal >>> 32 ) ) ) ; case REFERENCE : return HashCodes . hashInt ( ( ( Integer ) key ) . intValue ( ) ) ; case BYTES : return HashCodes . hashInt ( HashCodes . hashCode ( ( byte [ ] ) key ) ) ; case STRING : return HashCodes . hashInt ( key . hashCode ( ) ) ; case BOOLEAN : return HashCodes . hashInt ( ( ( Boolean ) key ) . booleanValue ( ) ? 1231 : 1237 ) ; case DOUBLE : long longBits = Double . doubleToRawLongBits ( ( ( Double ) key ) . doubleValue ( ) ) ; return HashCodes . hashInt ( ( int ) ( longBits ^ ( longBits >>> 32 ) ) ) ; case FLOAT : return HashCodes . hashInt ( Float . floatToRawIntBits ( ( ( Float ) key ) . floatValue ( ) ) ) ; default : throw new IllegalArgumentException ( "Unknown field type: " + fieldType ) ; } }


Input:
This method adds an element at nodeIndex . Note that this does not check for duplicates ; if the element already exists another instance of it will be added . This method is not thread - safe - you cannot call this method concurrently with itself or with { @link #getElements } . [CODESPLIT] public void addElement ( long nodeIndex , long element ) { if ( element > elementMask ) { throw new IllegalArgumentException ( "Element " + element + " does not fit in " + bitsPerElement + " bits" ) ; } if ( nodeIndex >= numNodes ) { throw new IllegalArgumentException ( "Provided nodeIndex  " + nodeIndex + " greater then numNodes " + numNodes ) ; } if ( element == NO_ELEMENT ) { // we use 0 to indicate an "empty" element, so we have to store ordinal zero here nodesWithOrdinalZero . setElementValue ( nodeIndex , 1 , 1 ) ; return ; } long bucketStart = nodeIndex * maxElementsPerNode * bitsPerElement ; long currentIndex ; int offset = 0 ; do { currentIndex = bucketStart + offset * bitsPerElement ; offset ++ ; } while ( storage . getElementValue ( currentIndex , bitsPerElement , elementMask ) != NO_ELEMENT && offset < maxElementsPerNode ) ; if ( storage . getElementValue ( currentIndex , bitsPerElement , elementMask ) != NO_ELEMENT ) { // we're full at this index - resize, then figure out the new current index resizeStorage ( ) ; currentIndex = nodeIndex * maxElementsPerNode * bitsPerElement + offset * bitsPerElement ; } /* we're adding to the first empty spot from the beginning of the bucket - this is
         * preferable to adding at the end because we want our getElements method to be fast, and
         * it's okay for addElement to be comparatively slow */ storage . setElementValue ( currentIndex , bitsPerElement , element ) ; }


Input: Return a list of elements at the specified node index . The returned list may contain duplicates . This method not thread - safe - the caller must ensure that no one calls { @link #addElement } concurrently with this method but calling this method concurrently with itself is safe . [CODESPLIT] public List < Long > getElements ( long nodeIndex ) { long bucketStart = nodeIndex * maxElementsPerNode * bitsPerElement ; List < Long > ret = new ArrayList <> ( ) ; if ( nodesWithOrdinalZero . getElementValue ( nodeIndex , 1 , 1 ) != NO_ELEMENT ) { // 0 indicates an "empty" element, so we fetch ordinal zeros from nodesWithOrdinalZero ret . add ( NO_ELEMENT ) ; } for ( int offset = 0 ; offset < maxElementsPerNode ; offset ++ ) { long element = storage . getElementValue ( bucketStart + offset * bitsPerElement , bitsPerElement , elementMask ) ; if ( element == NO_ELEMENT ) { break ; // we have exhausted the elements at this index } ret . add ( element ) ; } return ret ; }


Input: Resize the underlying storage to a multiple of what it currently is . This method is not thread - safe . [CODESPLIT] private void resizeStorage ( ) { int currentElementsPerNode = maxElementsPerNode ; int newElementsPerNode = ( int ) ( currentElementsPerNode * RESIZE_MULTIPLE ) ; if ( newElementsPerNode <= currentElementsPerNode ) { throw new IllegalStateException ( "cannot resize fixed length array from " + currentElementsPerNode + " to " + newElementsPerNode ) ; } FixedLengthElementArray newStorage = new FixedLengthElementArray ( memoryRecycler , numNodes * bitsPerElement * newElementsPerNode ) ; LongStream . range ( 0 , numNodes ) . forEach ( nodeIndex -> { long currentBucketStart = nodeIndex * currentElementsPerNode * bitsPerElement ; long newBucketStart = nodeIndex * newElementsPerNode * bitsPerElement ; for ( int offset = 0 ; offset < currentElementsPerNode ; offset ++ ) { long element = storage . getElementValue ( currentBucketStart + offset * bitsPerElement , bitsPerElement , elementMask ) ; if ( element == NO_ELEMENT ) { break ; // we have exhausted the elements at this index } newStorage . setElementValue ( newBucketStart + offset * bitsPerElement , bitsPerElement , element ) ; } } ) ; storage . destroy ( memoryRecycler ) ; storage = newStorage ; maxElementsPerNode = newElementsPerNode ; }


Input: / TODO : Many parse failures can cause out of memory errors . [CODESPLIT] protected void processFile ( Reader r , int maxSample ) throws Exception { JsonArrayChunker chunker = new JsonArrayChunker ( r , executor ) ; chunker . initialize ( ) ; int counter = 0 ; Reader jsonObj = chunker . nextChunk ( ) ; while ( jsonObj != null && counter < maxSample ) { final Reader currentObject = jsonObj ; executor . execute ( new Runnable ( ) { public void run ( ) { try { JsonFactory factory = new JsonFactory ( ) ; JsonParser parser = factory . createParser ( currentObject ) ; processRecord ( parser ) ; } catch ( Exception e ) { throw new RuntimeException ( e ) ; } } } ) ; while ( executor . getQueue ( ) . size ( ) > maxWorkQueue ) { Thread . sleep ( 5 ) ; } counter ++ ; jsonObj . close ( ) ; jsonObj = chunker . nextChunk ( ) ; } executor . awaitSuccessfulCompletion ( ) ; }


Input: Adds a sequence of bytes to this map . If the sequence of bytes has previously been added to this map then its assigned ordinal is returned . If the sequence of bytes has not been added to this map then a new ordinal is assigned and returned . <p > This operation is thread - safe . [CODESPLIT] public int getOrAssignOrdinal ( ByteDataBuffer serializedRepresentation , int preferredOrdinal ) { int hash = HashCodes . hashCode ( serializedRepresentation ) ; int ordinal = get ( serializedRepresentation , hash ) ; return ordinal != - 1 ? ordinal : assignOrdinal ( serializedRepresentation , hash , preferredOrdinal ) ; }


Input: / acquire the lock before writing . [CODESPLIT] private synchronized int assignOrdinal ( ByteDataBuffer serializedRepresentation , int hash , int preferredOrdinal ) { if ( preferredOrdinal < - 1 || preferredOrdinal > ORDINAL_MASK ) { throw new IllegalArgumentException ( String . format ( "The given preferred ordinal %s is out of bounds and not within the closed interval [-1, %s]" , preferredOrdinal , ORDINAL_MASK ) ) ; } if ( size > sizeBeforeGrow ) { growKeyArray ( ) ; } /// check to make sure that after acquiring the lock, the element still does not exist. /// this operation is akin to double-checked locking which is 'fixed' with the JSR 133 memory model in JVM >= 1.5. /// Note that this also requires pointersAndOrdinals be volatile so resizes are also visible AtomicLongArray pao = pointersAndOrdinals ; int modBitmask = pao . length ( ) - 1 ; int bucket = hash & modBitmask ; long key = pao . get ( bucket ) ; while ( key != EMPTY_BUCKET_VALUE ) { if ( compare ( serializedRepresentation , key ) ) { return ( int ) ( key >>> BITS_PER_POINTER ) ; } bucket = ( bucket + 1 ) & modBitmask ; key = pao . get ( bucket ) ; } /// the ordinal for this object still does not exist in the list, even after the lock has been acquired. /// it is up to this thread to add it at the current bucket position. int ordinal = findFreeOrdinal ( preferredOrdinal ) ; if ( ordinal > ORDINAL_MASK ) { throw new IllegalStateException ( String . format ( "Ordinal cannot be assigned. The to be assigned ordinal, %s, is greater than the maximum supported ordinal value of %s" , ordinal , ORDINAL_MASK ) ) ; } long pointer = byteData . length ( ) ; VarInt . writeVInt ( byteData , ( int ) serializedRepresentation . length ( ) ) ; /// Copying might cause a resize to the segmented array held by byteData /// A reading thread may observe a null value for a segment during the creation /// of a new segments array (see SegmentedByteArray.ensureCapacity). serializedRepresentation . copyTo ( byteData ) ; if ( byteData . length ( ) > MAX_BYTE_DATA_LENGTH ) { throw new IllegalStateException ( String . format ( "The number of bytes for the serialized representations, %s, is too large and is greater than the maximum of %s bytes" , byteData . length ( ) , MAX_BYTE_DATA_LENGTH ) ) ; } key = ( ( long ) ordinal << BITS_PER_POINTER ) | pointer ; size ++ ; /// this set on the AtomicLongArray has volatile semantics (i.e. behaves like a monitor release). /// Any other thread reading this element in the AtomicLongArray will have visibility to all memory writes this thread has made up to this point. /// This means the entire byte sequence is guaranteed to be visible to any thread which reads the pointer to that data. pao . set ( bucket , key ) ; return ordinal ; }


Input: If the preferredOrdinal has not already been used mark it and use it . Otherwise delegate to the FreeOrdinalTracker . [CODESPLIT] private int findFreeOrdinal ( int preferredOrdinal ) { if ( preferredOrdinal != - 1 && unusedPreviousOrdinals . get ( preferredOrdinal ) ) { unusedPreviousOrdinals . clear ( preferredOrdinal ) ; return preferredOrdinal ; } return freeOrdinalTracker . getFreeOrdinal ( ) ; }


Input: Create an array mapping the ordinals to pointers so that they can be easily looked up when writing to blob streams . [CODESPLIT] public void prepareForWrite ( ) { int maxOrdinal = 0 ; AtomicLongArray pao = pointersAndOrdinals ; for ( int i = 0 ; i < pao . length ( ) ; i ++ ) { long key = pao . get ( i ) ; if ( key != EMPTY_BUCKET_VALUE ) { int ordinal = ( int ) ( key >>> BITS_PER_POINTER ) ; if ( ordinal > maxOrdinal ) { maxOrdinal = ordinal ; } } } long [ ] pbo = new long [ maxOrdinal + 1 ] ; Arrays . fill ( pbo , - 1 ) ; for ( int i = 0 ; i < pao . length ( ) ; i ++ ) { long key = pao . get ( i ) ; if ( key != EMPTY_BUCKET_VALUE ) { int ordinal = ( int ) ( key >>> BITS_PER_POINTER ) ; pbo [ ordinal ] = key & POINTER_MASK ; } } pointersByOrdinal = pbo ; }


Input: Reclaim space in the byte array used in the previous cycle but not referenced in this cycle . <p > <p > This is achieved by shifting all used byte sequences down in the byte array then updating the key array to reflect the new pointers and exclude the removed entries . This is also where ordinals which are unused are returned to the pool . <p > [CODESPLIT] public void compact ( ThreadSafeBitSet usedOrdinals ) { long [ ] populatedReverseKeys = new long [ size ] ; int counter = 0 ; AtomicLongArray pao = pointersAndOrdinals ; for ( int i = 0 ; i < pao . length ( ) ; i ++ ) { long key = pao . get ( i ) ; if ( key != EMPTY_BUCKET_VALUE ) { populatedReverseKeys [ counter ++ ] = key << BITS_PER_ORDINAL | key >>> BITS_PER_POINTER ; } } Arrays . sort ( populatedReverseKeys ) ; SegmentedByteArray arr = byteData . getUnderlyingArray ( ) ; long currentCopyPointer = 0 ; for ( int i = 0 ; i < populatedReverseKeys . length ; i ++ ) { int ordinal = ( int ) ( populatedReverseKeys [ i ] & ORDINAL_MASK ) ; if ( usedOrdinals . get ( ordinal ) ) { long pointer = populatedReverseKeys [ i ] >>> BITS_PER_ORDINAL ; int length = VarInt . readVInt ( arr , pointer ) ; length += VarInt . sizeOfVInt ( length ) ; if ( currentCopyPointer != pointer ) { arr . copy ( arr , pointer , currentCopyPointer , length ) ; } populatedReverseKeys [ i ] = populatedReverseKeys [ i ] << BITS_PER_POINTER | currentCopyPointer ; currentCopyPointer += length ; } else { freeOrdinalTracker . returnOrdinalToPool ( ordinal ) ; populatedReverseKeys [ i ] = EMPTY_BUCKET_VALUE ; } } byteData . setPosition ( currentCopyPointer ) ; freeOrdinalTracker . sort ( ) ; // Reset the array then fill with compacted values // Volatile store not required, could use plain store // See VarHandles for JDK >= 9 for ( int i = 0 ; i < pao . length ( ) ; i ++ ) { pao . lazySet ( i , EMPTY_BUCKET_VALUE ) ; } populateNewHashArray ( pao , populatedReverseKeys ) ; size = usedOrdinals . cardinality ( ) ; pointersByOrdinal = null ; unusedPreviousOrdinals = null ; }


Input: Compare the byte sequence contained in the supplied ByteDataBuffer with the sequence contained in the map pointed to by the specified key byte by byte . [CODESPLIT] private boolean compare ( ByteDataBuffer serializedRepresentation , long key ) { long position = key & POINTER_MASK ; int sizeOfData = VarInt . readVInt ( byteData . getUnderlyingArray ( ) , position ) ; if ( sizeOfData != serializedRepresentation . length ( ) ) { return false ; } position += VarInt . sizeOfVInt ( sizeOfData ) ; for ( int i = 0 ; i < sizeOfData ; i ++ ) { if ( serializedRepresentation . get ( i ) != byteData . get ( position ++ ) ) { return false ; } } return true ; }


Input: Resize the ordinal map by increasing its capacity . <p > No action is take if the current capacity is sufficient for the given size . <p > WARNING : THIS OPERATION IS NOT THREAD - SAFE . [CODESPLIT] public void resize ( int size ) { size = bucketSize ( size ) ; if ( pointersAndOrdinals . length ( ) < size ) { growKeyArray ( size ) ; } }


Input: Grow the key array . All of the values in the current array must be re - hashed and added to the new array . [CODESPLIT] private void growKeyArray ( ) { int newSize = pointersAndOrdinals . length ( ) << 1 ; if ( newSize < 0 ) { throw new IllegalStateException ( "New size computed to grow the underlying array for the map is negative. " + "This is most likely due to the total number of keys added to map has exceeded the max capacity of the keys map can hold. " + "Current array size :" + pointersAndOrdinals . length ( ) + " and size to grow :" + newSize ) ; } growKeyArray ( newSize ) ; }


Input: Get the hash code for the byte array pointed to by the specified key . [CODESPLIT] private int rehashPreviouslyAddedData ( long key ) { long position = key & POINTER_MASK ; int sizeOfData = VarInt . readVInt ( byteData . getUnderlyingArray ( ) , position ) ; position += VarInt . sizeOfVInt ( sizeOfData ) ; return HashCodes . hashCode ( byteData . getUnderlyingArray ( ) , position , sizeOfData ) ; }


Input: Create an AtomicLongArray of the specified size each value in the array will be EMPTY_BUCKET_VALUE [CODESPLIT] private AtomicLongArray emptyKeyArray ( int size ) { AtomicLongArray arr = new AtomicLongArray ( size ) ; // Volatile store not required, could use plain store // See VarHandles for JDK >= 9 for ( int i = 0 ; i < arr . length ( ) ; i ++ ) { arr . lazySet ( i , EMPTY_BUCKET_VALUE ) ; } return arr ; }


Input: This method assumes the other traverser has the same match fields specified in the same order . [CODESPLIT] public boolean isMatchEqual ( int matchIdx , HollowIndexerValueTraverser otherTraverser , int otherMatchIdx ) { for ( int i = 0 ; i < getNumFieldPaths ( ) ; i ++ ) { if ( ! HollowReadFieldUtils . fieldsAreEqual ( ( HollowObjectTypeDataAccess ) fieldTypeDataAccess [ i ] , fieldMatchLists [ i ] . get ( matchIdx ) , fieldSchemaPosition [ i ] , ( HollowObjectTypeDataAccess ) otherTraverser . fieldTypeDataAccess [ i ] , otherTraverser . fieldMatchLists [ i ] . get ( otherMatchIdx ) , otherTraverser . fieldSchemaPosition [ i ] ) ) return false ; } return true ; }


Input: Warning : Not thread - safe . Should only be called within the update thread . [CODESPLIT] public int bitsRequiredForField ( String fieldName ) { int maxBitsRequiredForField = shards [ 0 ] . bitsRequiredForField ( fieldName ) ; for ( int i = 1 ; i < shards . length ; i ++ ) { int shardRequiredBits = shards [ i ] . bitsRequiredForField ( fieldName ) ; if ( shardRequiredBits > maxBitsRequiredForField ) maxBitsRequiredForField = shardRequiredBits ; } return maxBitsRequiredForField ; }


Input: Finds the unique object an instance of the unique type for a given key . [CODESPLIT] public T findMatch ( Q key ) { Object [ ] keyArray = matchFields . stream ( ) . map ( mf -> mf . extract ( key ) ) . toArray ( ) ; int ordinal = hpki . getMatchingOrdinal ( keyArray ) ; if ( ordinal == - 1 ) { return null ; } return uniqueTypeExtractor . extract ( api , ordinal ) ; }


Input: Starts the building of a { @link UniqueKeyIndex } . [CODESPLIT] public static < T extends HollowObject > Builder < T > from ( HollowConsumer consumer , Class < T > uniqueType ) { Objects . requireNonNull ( consumer ) ; Objects . requireNonNull ( uniqueType ) ; return new Builder <> ( consumer , uniqueType ) ; }


Input: Swap underlying state engines between current and pending while keeping the versions consistent ; used after delta integrity checks have altered the underlying state engines . [CODESPLIT] ReadStateHelper swap ( ) { return new ReadStateHelper ( newReadState ( current . getVersion ( ) , pending . getStateEngine ( ) ) , newReadState ( pending . getVersion ( ) , current . getStateEngine ( ) ) ) ; }


Input: Parse a collection of { @link HollowSchema } s from the provided Reader . [CODESPLIT] public static List < HollowSchema > parseCollectionOfSchemas ( Reader reader ) throws IOException { StreamTokenizer tokenizer = new StreamTokenizer ( reader ) ; configureTokenizer ( tokenizer ) ; List < HollowSchema > schemaList = new ArrayList < HollowSchema > ( ) ; HollowSchema schema = parseSchema ( tokenizer ) ; while ( schema != null ) { schemaList . add ( schema ) ; schema = parseSchema ( tokenizer ) ; } return schemaList ; }


Input: Parse a single { @link HollowSchema } from the provided String . [CODESPLIT] public static HollowSchema parseSchema ( String schema ) throws IOException { StreamTokenizer tokenizer = new StreamTokenizer ( new StringReader ( schema ) ) ; configureTokenizer ( tokenizer ) ; return parseSchema ( tokenizer ) ; }


Input: Calculates the type name from a given type . <p > If the type is annotated with { @link HollowTypeName } then the type name is the value of the { @code HollowTypeName . name } attribute . Otherwise the type name is derived from the type itself . If the type is a { @code Class } then the type name is the simple name of that class . If the type is a parameterized type and is assignable to a class of { @code List } { @code Set } or { @code Map } then the type name begins with the simple class name of the parameterized type s raw type followed by Of followed by the result of calling this method with the associated parameterized types ( in order in - fixed by To ) . Otherwise the type name is the simple class name of the parameterized type s raw type . <p > The translation from type to type name is lossy since the simple class name of a class is used . This means that no two types from different packages but with the same simple name can be utilized . [CODESPLIT] public static String getDefaultTypeName ( Type type ) { if ( type instanceof Class ) { Class < ? > clazz = ( Class < ? > ) type ; HollowTypeName explicitTypeName = clazz . getAnnotation ( HollowTypeName . class ) ; if ( explicitTypeName != null ) return explicitTypeName . name ( ) ; return clazz . getSimpleName ( ) ; } ParameterizedType parameterizedType = ( ParameterizedType ) type ; Class < ? > clazz = ( Class < ? > ) parameterizedType . getRawType ( ) ; if ( List . class . isAssignableFrom ( clazz ) ) return "ListOf" + getDefaultTypeName ( parameterizedType . getActualTypeArguments ( ) [ 0 ] ) ; if ( Set . class . isAssignableFrom ( clazz ) ) return "SetOf" + getDefaultTypeName ( parameterizedType . getActualTypeArguments ( ) [ 0 ] ) ; if ( Map . class . isAssignableFrom ( clazz ) ) return "MapOf" + getDefaultTypeName ( parameterizedType . getActualTypeArguments ( ) [ 0 ] ) + "To" + getDefaultTypeName ( parameterizedType . getActualTypeArguments ( ) [ 1 ] ) ; return clazz . getSimpleName ( ) ; }


Input: Determine size of hash table capable of storing the specified number of elements with a load factor applied . [CODESPLIT] public static int hashTableSize ( int numElements ) throws IllegalArgumentException { if ( numElements < 0 ) { throw new IllegalArgumentException ( "cannot be negative; numElements=" + numElements ) ; } else if ( numElements > HASH_TABLE_MAX_SIZE ) { throw new IllegalArgumentException ( "exceeds maximum number of buckets; numElements=" + numElements ) ; } if ( numElements == 0 ) return 1 ; if ( numElements < 3 ) return numElements * 2 ; // Apply load factor to number of elements and determine next // largest power of 2 that fits in an int int sizeAfterLoadFactor = ( int ) ( ( long ) numElements * 10 / 7 ) ; int bits = 32 - Integer . numberOfLeadingZeros ( sizeAfterLoadFactor - 1 ) ; return 1 << bits ; }


Input: Restores the data state to a desired version . <p > Data model { @link #initializeDataModel ( Class [] ) initialization } is required prior to restoring the producer . This ensures that restoration can correctly compare the producer s current data model with the data model of the restored data state and manage any differences in those models ( such as not restoring state for any types in the restoring data model not present in the producer s current data model ) [CODESPLIT] @ Override public HollowProducer . ReadState restore ( long versionDesired , HollowConsumer . BlobRetriever blobRetriever ) { return super . restore ( versionDesired , blobRetriever ) ; }


Input: Run a compaction cycle will produce a data state with exactly the same data as currently but reorganized so that ordinal holes are filled . This may need to be run multiple times to arrive at an optimal state . [CODESPLIT] public long runCompactionCycle ( HollowCompactor . CompactionConfig config ) { if ( config != null && readStates . hasCurrent ( ) ) { final HollowCompactor compactor = new HollowCompactor ( getWriteEngine ( ) , readStates . current ( ) . getStateEngine ( ) , config ) ; if ( compactor . needsCompaction ( ) ) { return runCycle ( newState -> compactor . compact ( ) ) ; } } return NO_ANNOUNCEMENT_AVAILABLE ; }


Input: Write the current state as a snapshot blob . [CODESPLIT] public void writeSnapshot ( OutputStream os ) throws IOException { stateEngine . prepareForWrite ( ) ; DataOutputStream dos = new DataOutputStream ( os ) ; writeHeader ( dos , stateEngine . getSchemas ( ) , false ) ; VarInt . writeVInt ( dos , stateEngine . getOrderedTypeStates ( ) . size ( ) ) ; SimultaneousExecutor executor = new SimultaneousExecutor ( getClass ( ) , "write-snapshot" ) ; for ( final HollowTypeWriteState typeState : stateEngine . getOrderedTypeStates ( ) ) { executor . execute ( new Runnable ( ) { public void run ( ) { typeState . calculateSnapshot ( ) ; } } ) ; } try { executor . awaitSuccessfulCompletion ( ) ; } catch ( Exception e ) { throw new RuntimeException ( e ) ; } for ( HollowTypeWriteState typeState : stateEngine . getOrderedTypeStates ( ) ) { HollowSchema schema = typeState . getSchema ( ) ; schema . writeTo ( dos ) ; writeNumShards ( dos , typeState . getNumShards ( ) ) ; typeState . writeSnapshot ( dos ) ; } os . flush ( ) ; }


Input: Serialize the changes necessary to transition a consumer from the previous state to the current state as a delta blob . [CODESPLIT] public void writeDelta ( OutputStream os ) throws IOException { stateEngine . prepareForWrite ( ) ; if ( stateEngine . isRestored ( ) ) stateEngine . ensureAllNecessaryStatesRestored ( ) ; List < HollowSchema > changedTypes = changedTypes ( ) ; DataOutputStream dos = new DataOutputStream ( os ) ; writeHeader ( dos , changedTypes , false ) ; VarInt . writeVInt ( dos , changedTypes . size ( ) ) ; SimultaneousExecutor executor = new SimultaneousExecutor ( getClass ( ) , "write-delta" ) ; for ( final HollowTypeWriteState typeState : stateEngine . getOrderedTypeStates ( ) ) { executor . execute ( new Runnable ( ) { public void run ( ) { if ( typeState . hasChangedSinceLastCycle ( ) ) typeState . calculateDelta ( ) ; } } ) ; } try { executor . awaitSuccessfulCompletion ( ) ; } catch ( Exception e ) { throw new RuntimeException ( e ) ; } for ( HollowTypeWriteState typeState : stateEngine . getOrderedTypeStates ( ) ) { if ( typeState . hasChangedSinceLastCycle ( ) ) { HollowSchema schema = typeState . getSchema ( ) ; schema . writeTo ( dos ) ; writeNumShards ( dos , typeState . getNumShards ( ) ) ; typeState . writeDelta ( dos ) ; } } os . flush ( ) ; }


Input: Adds the schema name to the set if the schema name doesn t correspond to a Hollow primitive type . Factored out to prevent bloat in the switch statement it is called from . [CODESPLIT] private void addToSetIfNotPrimitiveOrCollection ( Set < String > schemaNameSet , String ... schemaNames ) { for ( String schemaName : schemaNames ) { // collections schemas get brought in by a star import if ( ! HollowCodeGenerationUtils . isCollectionType ( schemaName , dataset ) && ! HollowCodeGenerationUtils . isPrimitiveType ( schemaName ) ) { schemaNameSet . add ( schemaName ) ; } } }


Input: Create a new state version . <p > [CODESPLIT] public long mint ( ) { SimpleDateFormat dateFormat = new SimpleDateFormat ( "yyyyMMddHHmmss" ) ; dateFormat . setTimeZone ( TimeZone . getTimeZone ( "UTC" ) ) ; String formattedDate = dateFormat . format ( new Date ( ) ) ; String versionStr = formattedDate + String . format ( "%03d" , versionCounter . incrementAndGet ( ) % 1000 ) ; return Long . parseLong ( versionStr ) ; }


Input: Registers { @code DuplicateDataDetectionValidator } validators with the given { @link HollowProducer producer } for all object schema declared with a primary key . <p > This requires that the producer s data model has been initialized ( see { @link HollowProducer#initializeDataModel ( Class [] ) } or a prior run cycle has implicitly initialized the data model . <p > For each { @link HollowTypeWriteState write state } that has a { @link HollowObjectSchema object schema } declared with a { @link PrimaryKey primary key } a { @code DuplicateDataDetectionValidator } validator is instantiated with the primary key type name and registered with the given producer ( if a { @code DuplicateDataDetectionValidator } validator is not already registered for the same primary key type name ) . [CODESPLIT] public static void addValidatorsForSchemaWithPrimaryKey ( HollowProducer producer ) { producer . getWriteEngine ( ) . getOrderedTypeStates ( ) . stream ( ) . filter ( ts -> ts . getSchema ( ) . getSchemaType ( ) == SchemaType . OBJECT ) . map ( ts -> ( HollowObjectSchema ) ts . getSchema ( ) ) . filter ( hos -> hos . getPrimaryKey ( ) != null ) . map ( HollowObjectSchema :: getPrimaryKey ) . forEach ( k -> producer . addListener ( new DuplicateDataDetectionValidator ( k . getType ( ) ) ) ) ; }


Input: Rules : prepend get / is + upper case first char of field name [CODESPLIT] public static String generateAccessortMethodName ( String fieldName , Class < ? > clazz ) { String prefix = "get" ; if ( boolean . class . equals ( clazz ) || Boolean . class . equals ( clazz ) ) { for ( String booleanPrefix : booleanMethodPrefixes ) { if ( fieldName . startsWith ( booleanPrefix ) && fieldName . length ( ) > booleanPrefix . length ( ) ) { char firstCharAfterBooleanPrefix = fieldName . charAt ( booleanPrefix . length ( ) ) ; if ( Character . isUpperCase ( firstCharAfterBooleanPrefix ) ) { return fieldName ; } } } } return substituteInvalidChars ( prefix + uppercase ( fieldName ) ) ; }


Input: Convert field path into Param name [CODESPLIT] public static String normalizeFieldPathToParamName ( String fieldPath ) { String result = null ; if ( fieldPath . contains ( "." ) ) { String [ ] parts = fieldPath . split ( "\\." ) ; StringBuilder sb = new StringBuilder ( ) ; sb . append ( lowercase ( parts [ 0 ] ) ) ; for ( int i = 1 ; i < parts . length ; i ++ ) { sb . append ( uppercase ( parts [ i ] ) ) ; } result = sb . toString ( ) ; } else { result = lowercase ( fieldPath ) ; } if ( result . endsWith ( "!" ) ) { return result . substring ( 0 , result . length ( ) - 1 ) ; } return result ; }


Input: Recreate the hash index entirely [CODESPLIT] private void reindexHashIndex ( ) { HollowHashIndexBuilder builder = new HollowHashIndexBuilder ( stateEngine , type , selectField , matchFields ) ; builder . buildIndex ( ) ; this . hashStateVolatile = new HollowHashIndexState ( builder ) ; }


Input: Query the index . [CODESPLIT] public HollowHashIndexResult findMatches ( Object ... query ) { int hashCode = 0 ; for ( int i = 0 ; i < query . length ; i ++ ) { if ( query [ i ] == null ) throw new IllegalArgumentException ( "querying by null unsupported; i=" + i ) ; hashCode ^= HashCodes . hashInt ( keyHashCode ( query [ i ] , i ) ) ; } HollowHashIndexResult result ; HollowHashIndexState hashState ; do { result = null ; hashState = hashStateVolatile ; long bucket = hashCode & hashState . getMatchHashMask ( ) ; long hashBucketBit = bucket * hashState . getBitsPerMatchHashEntry ( ) ; boolean bucketIsEmpty = hashState . getMatchHashTable ( ) . getElementValue ( hashBucketBit , hashState . getBitsPerTraverserField ( ) [ 0 ] ) == 0 ; while ( ! bucketIsEmpty ) { if ( matchIsEqual ( hashState . getMatchHashTable ( ) , hashBucketBit , query ) ) { int selectSize = ( int ) hashState . getMatchHashTable ( ) . getElementValue ( hashBucketBit + hashState . getBitsPerMatchHashKey ( ) , hashState . getBitsPerSelectTableSize ( ) ) ; long selectBucketPointer = hashState . getMatchHashTable ( ) . getElementValue ( hashBucketBit + hashState . getBitsPerMatchHashKey ( ) + hashState . getBitsPerSelectTableSize ( ) , hashState . getBitsPerSelectTablePointer ( ) ) ; result = new HollowHashIndexResult ( hashState , selectBucketPointer , selectSize ) ; break ; } bucket = ( bucket + 1 ) & hashState . getMatchHashMask ( ) ; hashBucketBit = bucket * hashState . getBitsPerMatchHashEntry ( ) ; bucketIsEmpty = hashState . getMatchHashTable ( ) . getElementValue ( hashBucketBit , hashState . getBitsPerTraverserField ( ) [ 0 ] ) == 0 ; } } while ( hashState != hashStateVolatile ) ; return result ; }


Input: Triggers async refresh after some random number of milliseconds have passed between now and the specified maximum number of milliseconds . [CODESPLIT] public void triggerAsyncRefreshWithRandomDelay ( int maxDelayMillis ) { Random rand = new Random ( ) ; int delayMillis = maxDelayMillis > 0 ? rand . nextInt ( maxDelayMillis ) : 0 ; triggerAsyncRefreshWithDelay ( delayMillis ) ; }


Input: Triggers async refresh after the specified number of milliseconds has passed . [CODESPLIT] public void triggerAsyncRefreshWithDelay ( int delayMillis ) { final HollowClient client = this . client ; final long targetBeginTime = System . currentTimeMillis ( ) + delayMillis ; refreshExecutor . execute ( new Runnable ( ) { public void run ( ) { try { long delay = targetBeginTime - System . currentTimeMillis ( ) ; if ( delay > 0 ) Thread . sleep ( delay ) ; client . triggerRefresh ( ) ; } catch ( Throwable th ) { log . log ( Level . SEVERE , "Async refresh failed" , th ) ; } } } ) ; }


Input: Add a type to be included in the diff report [CODESPLIT] public HollowTypeDiff addTypeDiff ( String type , String ... primaryKeyPaths ) { HollowTypeDiff typeDiff = new HollowTypeDiff ( this , type , primaryKeyPaths ) ; if ( typeDiff . hasAnyData ( ) ) typeDiffs . put ( type , typeDiff ) ; return typeDiff ; }


Input: Run the diff [CODESPLIT] public void calculateDiffs ( ) { long startTime = System . currentTimeMillis ( ) ; prepareForDiffCalculation ( ) ; long endTime = System . currentTimeMillis ( ) ; log . info ( "PREPARED IN " + ( endTime - startTime ) + "ms" ) ; for ( HollowTypeDiff typeDiff : typeDiffs . values ( ) ) { typeDiff . calculateDiffs ( ) ; } }


Input: Call this method after each time a delta occurs in the backing { @link HollowReadStateEngine } . This is how the HollowHistory knows how to create a new { @link HollowHistoricalState } . [CODESPLIT] public void deltaOccurred ( long newVersion ) { keyIndex . update ( latestHollowReadStateEngine , true ) ; HollowHistoricalStateDataAccess historicalDataAccess = creator . createBasedOnNewDelta ( latestVersion , latestHollowReadStateEngine ) ; historicalDataAccess . setNextState ( latestHollowReadStateEngine ) ; HollowHistoricalStateKeyOrdinalMapping keyOrdinalMapping = createKeyOrdinalMappingFromDelta ( ) ; HollowHistoricalState historicalState = new HollowHistoricalState ( newVersion , keyOrdinalMapping , historicalDataAccess , latestHeaderEntries ) ; addHistoricalState ( historicalState ) ; this . latestVersion = newVersion ; this . latestHeaderEntries = latestHollowReadStateEngine . getHeaderTags ( ) ; }


Input: Call this method after each time a double snapshot occurs . <p > This method will replace the previous backing { @link HollowReadStateEngine } with the newly supplied one stitch together all of the existing history with the new state currently in the new { @link HollowReadStateEngine } and create a new { @link HollowHistoricalState } to represent the transition . [CODESPLIT] public void doubleSnapshotOccurred ( HollowReadStateEngine newHollowStateEngine , long newVersion ) { if ( ! keyIndex . isInitialized ( ) ) keyIndex . update ( latestHollowReadStateEngine , false ) ; keyIndex . update ( newHollowStateEngine , false ) ; HollowHistoricalStateDataAccess historicalDataAccess ; DiffEqualityMapping mapping = new DiffEqualityMapping ( latestHollowReadStateEngine , newHollowStateEngine , true , ! ignoreListOrderingOnDoubleSnapshot ) ; DiffEqualityMappingOrdinalRemapper remapper = new DiffEqualityMappingOrdinalRemapper ( mapping ) ; historicalDataAccess = creator . createHistoricalStateFromDoubleSnapshot ( latestVersion , latestHollowReadStateEngine , newHollowStateEngine , remapper ) ; HollowHistoricalStateDataAccess nextRemappedDataAccess = historicalDataAccess ; HollowHistoricalState nextRemappedState = null ; HollowHistoricalStateDataAccess [ ] remappedDataAccesses = new HollowHistoricalStateDataAccess [ historicalStates . size ( ) ] ; HollowHistoricalStateKeyOrdinalMapping [ ] remappedKeyOrdinalMappings = new HollowHistoricalStateKeyOrdinalMapping [ historicalStates . size ( ) ] ; remapHistoricalStateOrdinals ( remapper , remappedDataAccesses , remappedKeyOrdinalMappings ) ; for ( int i = 0 ; i < historicalStates . size ( ) ; i ++ ) { HollowHistoricalState historicalStateToRemap = historicalStates . get ( i ) ; HollowHistoricalStateDataAccess remappedDataAccess = remappedDataAccesses [ i ] ; HollowHistoricalStateKeyOrdinalMapping remappedKeyOrdinalMapping = remappedKeyOrdinalMappings [ i ] ; remappedDataAccess . setNextState ( nextRemappedDataAccess ) ; nextRemappedDataAccess = remappedDataAccess ; HollowHistoricalState remappedState = new HollowHistoricalState ( historicalStateToRemap . getVersion ( ) , remappedKeyOrdinalMapping , remappedDataAccess , historicalStateToRemap . getHeaderEntries ( ) ) ; remappedState . setNextState ( nextRemappedState ) ; nextRemappedState = remappedState ; historicalStates . set ( i , remappedState ) ; historicalStateLookupMap . put ( remappedState . getVersion ( ) , remappedState ) ; } historicalDataAccess . setNextState ( newHollowStateEngine ) ; HollowHistoricalStateKeyOrdinalMapping keyOrdinalMapping = createKeyOrdinalMappingFromDoubleSnapshot ( newHollowStateEngine , remapper ) ; HollowHistoricalState historicalState = new HollowHistoricalState ( newVersion , keyOrdinalMapping , historicalDataAccess , latestHeaderEntries ) ; addHistoricalState ( historicalState ) ; this . latestVersion = newVersion ; this . latestHollowReadStateEngine = newHollowStateEngine ; this . latestHeaderEntries = latestHollowReadStateEngine . getHeaderTags ( ) ; }


Input: Removes the last { @code n } historical states . [CODESPLIT] public void removeHistoricalStates ( int n ) { if ( n < 0 ) { throw new IllegalArgumentException ( String . format ( "Number of states to remove is negative: %d" , n ) ) ; } if ( n > historicalStates . size ( ) ) { throw new IllegalArgumentException ( String . format ( "Number of states to remove, %d, is greater than the number of states. %d" , n , historicalStates . size ( ) ) ) ; } while ( n -- > 0 ) { HollowHistoricalState removedState = historicalStates . remove ( historicalStates . size ( ) - 1 ) ; historicalStateLookupMap . remove ( removedState . getVersion ( ) ) ; } }


Input: Encode the specified long as a variable length integer into the supplied { @link ByteDataBuffer } [CODESPLIT] public static void writeVLong ( ByteDataBuffer buf , long value ) { if ( value < 0 ) buf . write ( ( byte ) 0x81 ) ; if ( value > 0xFFFFFFFFFFFFFF L || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 56 ) & 0x7F L ) ) ) ; if ( value > 0x1FFFFFFFFFFFF L || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 49 ) & 0x7F L ) ) ) ; if ( value > 0x3FFFFFFFFFF L || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 42 ) & 0x7F L ) ) ) ; if ( value > 0x7FFFFFFFF L || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 35 ) & 0x7F L ) ) ) ; if ( value > 0xFFFFFFF L || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 28 ) & 0x7F L ) ) ) ; if ( value > 0x1FFFFF L || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 21 ) & 0x7F L ) ) ) ; if ( value > 0x3FFF L || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 14 ) & 0x7F L ) ) ) ; if ( value > 0x7F L || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 7 ) & 0x7F L ) ) ) ; buf . write ( ( byte ) ( value & 0x7F L ) ) ; }


Input: Encode the specified long as a variable length integer into the supplied OuputStream [CODESPLIT] public static void writeVLong ( OutputStream out , long value ) throws IOException { if ( value < 0 ) out . write ( ( byte ) 0x81 ) ; if ( value > 0xFFFFFFFFFFFFFF L || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 56 ) & 0x7F L ) ) ) ; if ( value > 0x1FFFFFFFFFFFF L || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 49 ) & 0x7F L ) ) ) ; if ( value > 0x3FFFFFFFFFF L || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 42 ) & 0x7F L ) ) ) ; if ( value > 0x7FFFFFFFF L || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 35 ) & 0x7F L ) ) ) ; if ( value > 0xFFFFFFF L || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 28 ) & 0x7F L ) ) ) ; if ( value > 0x1FFFFF L || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 21 ) & 0x7F L ) ) ) ; if ( value > 0x3FFF L || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 14 ) & 0x7F L ) ) ) ; if ( value > 0x7F L || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 7 ) & 0x7F L ) ) ) ; out . write ( ( byte ) ( value & 0x7F L ) ) ; }


Input: Encode the specified int as a variable length integer into the supplied { @link ByteDataBuffer } [CODESPLIT] public static void writeVInt ( ByteDataBuffer buf , int value ) { if ( value > 0x0FFFFFFF || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 28 ) ) ) ) ; if ( value > 0x1FFFFF || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 21 ) & 0x7F ) ) ) ; if ( value > 0x3FFF || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 14 ) & 0x7F ) ) ) ; if ( value > 0x7F || value < 0 ) buf . write ( ( byte ) ( 0x80 | ( ( value >>> 7 ) & 0x7F ) ) ) ; buf . write ( ( byte ) ( value & 0x7F ) ) ; }


Input: Encode the specified int as a variable length integer into the supplied OutputStream [CODESPLIT] public static void writeVInt ( OutputStream out , int value ) throws IOException { if ( value > 0x0FFFFFFF || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 28 ) ) ) ) ; if ( value > 0x1FFFFF || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 21 ) & 0x7F ) ) ) ; if ( value > 0x3FFF || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 14 ) & 0x7F ) ) ) ; if ( value > 0x7F || value < 0 ) out . write ( ( byte ) ( 0x80 | ( ( value >>> 7 ) & 0x7F ) ) ) ; out . write ( ( byte ) ( value & 0x7F ) ) ; }


Input: Write the value as a VarInt into the array starting at the specified position . [CODESPLIT] public static int writeVInt ( byte data [ ] , int pos , int value ) { if ( value > 0x0FFFFFFF || value < 0 ) data [ pos ++ ] = ( ( byte ) ( 0x80 | ( ( value >>> 28 ) ) ) ) ; if ( value > 0x1FFFFF || value < 0 ) data [ pos ++ ] = ( ( byte ) ( 0x80 | ( ( value >>> 21 ) & 0x7F ) ) ) ; if ( value > 0x3FFF || value < 0 ) data [ pos ++ ] = ( ( byte ) ( 0x80 | ( ( value >>> 14 ) & 0x7F ) ) ) ; if ( value > 0x7F || value < 0 ) data [ pos ++ ] = ( ( byte ) ( 0x80 | ( ( value >>> 7 ) & 0x7F ) ) ) ; data [ pos ++ ] = ( byte ) ( value & 0x7F ) ; return pos ; }


Input: Read a variable length integer from the supplied { [CODESPLIT] public static int readVInt ( ByteData arr , long position ) { byte b = arr . get ( position ++ ) ; if ( b == ( byte ) 0x80 ) throw new RuntimeException ( "Attempting to read null value as int" ) ; int value = b & 0x7F ; while ( ( b & 0x80 ) != 0 ) { b = arr . get ( position ++ ) ; value <<= 7 ; value |= ( b & 0x7F ) ; } return value ; }


Input: Read a variable length integer from the supplied InputStream [CODESPLIT] public static int readVInt ( InputStream in ) throws IOException { byte b = ( byte ) in . read ( ) ; if ( b == ( byte ) 0x80 ) throw new RuntimeException ( "Attempting to read null value as int" ) ; int value = b & 0x7F ; while ( ( b & 0x80 ) != 0 ) { b = ( byte ) in . read ( ) ; value <<= 7 ; value |= ( b & 0x7F ) ; } return value ; }


Input: Read a variable length long from the supplied { [CODESPLIT] public static long readVLong ( ByteData arr , long position ) { byte b = arr . get ( position ++ ) ; if ( b == ( byte ) 0x80 ) throw new RuntimeException ( "Attempting to read null value as long" ) ; long value = b & 0x7F ; while ( ( b & 0x80 ) != 0 ) { b = arr . get ( position ++ ) ; value <<= 7 ; value |= ( b & 0x7F ) ; } return value ; }


Input: Determine the size ( in bytes ) of the variable length long in the supplied { [CODESPLIT] public static int nextVLongSize ( ByteData arr , long position ) { byte b = arr . get ( position ++ ) ; if ( b == ( byte ) 0x80 ) return 1 ; int length = 1 ; while ( ( b & 0x80 ) != 0 ) { b = arr . get ( position ++ ) ; length ++ ; } return length ; }


Input: Read a variable length long from the supplied InputStream . [CODESPLIT] public static long readVLong ( InputStream in ) throws IOException { byte b = ( byte ) in . read ( ) ; if ( b == ( byte ) 0x80 ) throw new RuntimeException ( "Attempting to read null value as long" ) ; long value = b & 0x7F ; while ( ( b & 0x80 ) != 0 ) { b = ( byte ) in . read ( ) ; value <<= 7 ; value |= ( b & 0x7F ) ; } return value ; }


Input: Determine the size ( in bytes ) of the specified value when encoded as a variable length integer . [CODESPLIT] public static int sizeOfVLong ( long value ) { if ( value < 0L ) return 10 ; if ( value < 0x80 L ) return 1 ; if ( value < 0x4000 L ) return 2 ; if ( value < 0x200000 L ) return 3 ; if ( value < 0x10000000 L ) return 4 ; if ( value < 0x800000000 L ) return 5 ; if ( value < 0x40000000000 L ) return 6 ; if ( value < 0x2000000000000 L ) return 7 ; if ( value < 0x100000000000000 L ) return 8 ; return 9 ; }


Input: Count the number of variable length integers encoded in the supplied { [CODESPLIT] public static int countVarIntsInRange ( ByteData byteData , long fieldPosition , int length ) { int numInts = 0 ; boolean insideInt = false ; for ( int i = 0 ; i < length ; i ++ ) { byte b = byteData . get ( fieldPosition + i ) ; if ( ( b & 0x80 ) == 0 ) { numInts ++ ; insideInt = false ; } else if ( ! insideInt && b == ( byte ) 0x80 ) { numInts ++ ; } else { insideInt = true ; } } return numInts ; }


Input: Called when the activity is first created . [CODESPLIT] @ Override public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_ptr_expandable_list ) ; mPullRefreshListView = ( PullToRefreshExpandableListView ) findViewById ( R . id . pull_refresh_expandable_list ) ; // Set a listener to be invoked when the list should be refreshed. mPullRefreshListView . setOnRefreshListener ( new OnRefreshListener < ExpandableListView > ( ) { @ Override public void onRefresh ( PullToRefreshBase < ExpandableListView > refreshView ) { // Do work to refresh the list here. new GetDataTask ( ) . execute ( ) ; } } ) ; for ( String group : mGroupStrings ) { Map < String , String > groupMap1 = new HashMap < String , String > ( ) ; groupData . add ( groupMap1 ) ; groupMap1 . put ( KEY , group ) ; List < Map < String , String > > childList = new ArrayList < Map < String , String > > ( ) ; for ( String string : mChildStrings ) { Map < String , String > childMap = new HashMap < String , String > ( ) ; childList . add ( childMap ) ; childMap . put ( KEY , string ) ; } childData . add ( childList ) ; } mAdapter = new SimpleExpandableListAdapter ( this , groupData , android . R . layout . simple_expandable_list_item_1 , new String [ ] { KEY } , new int [ ] { android . R . id . text1 } , childData , android . R . layout . simple_expandable_list_item_2 , new String [ ] { KEY } , new int [ ] { android . R . id . text1 } ) ; setListAdapter ( mAdapter ) ; }


Input: Used internally for adding view . Need because we override addView to pass - through to the Refreshable View [CODESPLIT] protected final void addViewInternal ( View child , int index , ViewGroup . LayoutParams params ) { super . addView ( child , index , params ) ; }


Input: Used internally for { [CODESPLIT] protected LoadingLayoutProxy createLoadingLayoutProxy ( final boolean includeStart , final boolean includeEnd ) { LoadingLayoutProxy proxy = new LoadingLayoutProxy ( ) ; if ( includeStart && mMode . showHeaderLoadingLayout ( ) ) { proxy . addLayout ( mHeaderLayout ) ; } if ( includeEnd && mMode . showFooterLoadingLayout ( ) ) { proxy . addLayout ( mFooterLayout ) ; } return proxy ; }


Input: Called when the UI has been to be updated to be in the { @link State#REFRESHING } or { @link State#MANUAL_REFRESHING } state . [CODESPLIT] protected void onRefreshing ( final boolean doScroll ) { if ( mMode . showHeaderLoadingLayout ( ) ) { mHeaderLayout . refreshing ( ) ; } if ( mMode . showFooterLoadingLayout ( ) ) { mFooterLayout . refreshing ( ) ; } if ( doScroll ) { if ( mShowViewWhileRefreshing ) { // Call Refresh Listener when the Scroll has finished OnSmoothScrollFinishedListener listener = new OnSmoothScrollFinishedListener ( ) { @ Override public void onSmoothScrollFinished ( ) { callRefreshListener ( ) ; } } ; switch ( mCurrentMode ) { case MANUAL_REFRESH_ONLY : case PULL_FROM_END : smoothScrollTo ( getFooterSize ( ) , listener ) ; break ; default : case PULL_FROM_START : smoothScrollTo ( - getHeaderSize ( ) , listener ) ; break ; } } else { smoothScrollTo ( 0 ) ; } } else { // We're not scrolling, so just call Refresh Listener now callRefreshListener ( ) ; } }


Input: Called when the UI has been to be updated to be in the { [CODESPLIT] protected void onReset ( ) { mIsBeingDragged = false ; mLayoutVisibilityChangesEnabled = true ; // Always reset both layouts, just in case... mHeaderLayout . reset ( ) ; mFooterLayout . reset ( ) ; smoothScrollTo ( 0 ) ; }


Input: Re - measure the Loading Views height and adjust internal padding as necessary [CODESPLIT] protected final void refreshLoadingViewsSize ( ) { final int maximumPullScroll = ( int ) ( getMaximumPullScroll ( ) * 1.2f ) ; int pLeft = getPaddingLeft ( ) ; int pTop = getPaddingTop ( ) ; int pRight = getPaddingRight ( ) ; int pBottom = getPaddingBottom ( ) ; switch ( getPullToRefreshScrollDirection ( ) ) { case HORIZONTAL : if ( mMode . showHeaderLoadingLayout ( ) ) { mHeaderLayout . setWidth ( maximumPullScroll ) ; pLeft = - maximumPullScroll ; } else { pLeft = 0 ; } if ( mMode . showFooterLoadingLayout ( ) ) { mFooterLayout . setWidth ( maximumPullScroll ) ; pRight = - maximumPullScroll ; } else { pRight = 0 ; } break ; case VERTICAL : if ( mMode . showHeaderLoadingLayout ( ) ) { mHeaderLayout . setHeight ( maximumPullScroll ) ; pTop = - maximumPullScroll ; } else { pTop = 0 ; } if ( mMode . showFooterLoadingLayout ( ) ) { mFooterLayout . setHeight ( maximumPullScroll ) ; pBottom = - maximumPullScroll ; } else { pBottom = 0 ; } break ; } if ( DEBUG ) { Log . d ( LOG_TAG , String . format ( "Setting Padding. L: %d, T: %d, R: %d, B: %d" , pLeft , pTop , pRight , pBottom ) ) ; } setPadding ( pLeft , pTop , pRight , pBottom ) ; }


Input:
Helper method which just calls scrollTo () in the correct scrolling direction . [CODESPLIT] protected final void setHeaderScroll ( int value ) { if ( DEBUG ) { Log . d ( LOG_TAG , "setHeaderScroll: " + value ) ; } // Clamp value to with pull scroll range final int maximumPullScroll = getMaximumPullScroll ( ) ; value = Math . min ( maximumPullScroll , Math . max ( - maximumPullScroll , value ) ) ; if ( mLayoutVisibilityChangesEnabled ) { if ( value < 0 ) { mHeaderLayout . setVisibility ( View . VISIBLE ) ; } else if ( value > 0 ) { mFooterLayout . setVisibility ( View . VISIBLE ) ; } else { mHeaderLayout . setVisibility ( View . INVISIBLE ) ; mFooterLayout . setVisibility ( View . INVISIBLE ) ; } } if ( USE_HW_LAYERS ) { /**
			 * Use a Hardware Layer on the Refreshable View if we've scrolled at
			 * all. We don't use them on the Header/Footer Views as they change
			 * often, which would negate any HW layer performance boost.
			 */ ViewCompat . setLayerType ( mRefreshableViewWrapper , value != 0 ? View . LAYER_TYPE_HARDWARE : View . LAYER_TYPE_NONE ) ; } switch ( getPullToRefreshScrollDirection ( ) ) { case VERTICAL : scrollTo ( 0 , value ) ; break ; case HORIZONTAL : scrollTo ( value , 0 ) ; break ; } }


Input: Updates the View State when the mode has been set . This does not do any checking that the mode is different to current state so always updates . [CODESPLIT] protected void updateUIForMode ( ) { // We need to use the correct LayoutParam values, based on scroll // direction final LinearLayout . LayoutParams lp = getLoadingLayoutLayoutParams ( ) ; // Remove Header, and then add Header Loading View again if needed if ( this == mHeaderLayout . getParent ( ) ) { removeView ( mHeaderLayout ) ; } if ( mMode . showHeaderLoadingLayout ( ) ) { addViewInternal ( mHeaderLayout , 0 , lp ) ; } // Remove Footer, and then add Footer Loading View again if needed if ( this == mFooterLayout . getParent ( ) ) { removeView ( mFooterLayout ) ; } if ( mMode . showFooterLoadingLayout ( ) ) { addViewInternal ( mFooterLayout , lp ) ; } // Hide Loading Views refreshLoadingViewsSize ( ) ; // If we're not using Mode.BOTH, set mCurrentMode to mMode, otherwise // set it to pull down mCurrentMode = ( mMode != Mode . BOTH ) ? mMode : Mode . PULL_FROM_START ; }


Input: Actions a Pull Event [CODESPLIT] private void pullEvent ( ) { final int newScrollValue ; final int itemDimension ; final float initialMotionValue , lastMotionValue ; switch ( getPullToRefreshScrollDirection ( ) ) { case HORIZONTAL : initialMotionValue = mInitialMotionX ; lastMotionValue = mLastMotionX ; break ; case VERTICAL : default : initialMotionValue = mInitialMotionY ; lastMotionValue = mLastMotionY ; break ; } switch ( mCurrentMode ) { case PULL_FROM_END : newScrollValue = Math . round ( Math . max ( initialMotionValue - lastMotionValue , 0 ) / FRICTION ) ; itemDimension = getFooterSize ( ) ; break ; case PULL_FROM_START : default : newScrollValue = Math . round ( Math . min ( initialMotionValue - lastMotionValue , 0 ) / FRICTION ) ; itemDimension = getHeaderSize ( ) ; break ; } setHeaderScroll ( newScrollValue ) ; if ( newScrollValue != 0 && ! isRefreshing ( ) ) { float scale = Math . abs ( newScrollValue ) / ( float ) itemDimension ; switch ( mCurrentMode ) { case PULL_FROM_END : mFooterLayout . onPull ( scale ) ; break ; case PULL_FROM_START : default : mHeaderLayout . onPull ( scale ) ; break ; } if ( mState != State . PULL_TO_REFRESH && itemDimension >= Math . abs ( newScrollValue ) ) { setState ( State . PULL_TO_REFRESH ) ; } else if ( mState == State . PULL_TO_REFRESH && itemDimension < Math . abs ( newScrollValue ) ) { setState ( State . RELEASE_TO_REFRESH ) ; } } }


Input: Called when the activity is first created . [CODESPLIT] @ Override public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_ptr_horizontalscrollview ) ; mPullRefreshScrollView = ( PullToRefreshHorizontalScrollView ) findViewById ( R . id . pull_refresh_horizontalscrollview ) ; mPullRefreshScrollView . setOnRefreshListener ( new OnRefreshListener < HorizontalScrollView > ( ) { @ Override public void onRefresh ( PullToRefreshBase < HorizontalScrollView > refreshView ) { new GetDataTask ( ) . execute ( ) ; } } ) ; mScrollView = mPullRefreshScrollView . getRefreshableView ( ) ; }


Input: Called when the activity is first created . [CODESPLIT] @ Override public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_ptr_list_fragment ) ; mPullRefreshListFragment = ( PullToRefreshListFragment ) getSupportFragmentManager ( ) . findFragmentById ( R . id . frag_ptr_list ) ; // Get PullToRefreshListView from Fragment mPullRefreshListView = mPullRefreshListFragment . getPullToRefreshListView ( ) ; // Set a listener to be invoked when the list should be refreshed. mPullRefreshListView . setOnRefreshListener ( this ) ; // You can also just use mPullRefreshListFragment.getListView() ListView actualListView = mPullRefreshListView . getRefreshableView ( ) ; mListItems = new LinkedList < String > ( ) ; mListItems . addAll ( Arrays . asList ( mStrings ) ) ; mAdapter = new ArrayAdapter < String > ( this , android . R . layout . simple_list_item_1 , mListItems ) ; // You can also just use setListAdapter(mAdapter) or // mPullRefreshListView.setAdapter(mAdapter) actualListView . setAdapter ( mAdapter ) ; mPullRefreshListFragment . setListShown ( true ) ; }


Input: Called when the activity is first created . [CODESPLIT] @ Override public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_ptr_webview2 ) ; PullToRefreshWebView2 pullRefreshWebView = ( PullToRefreshWebView2 ) findViewById ( R . id . pull_refresh_webview2 ) ; pullRefreshWebView . setOnRefreshListener ( this ) ; WebView webView = pullRefreshWebView . getRefreshableView ( ) ; webView . getSettings ( ) . setJavaScriptEnabled ( true ) ; webView . setWebViewClient ( new SampleWebViewClient ( ) ) ; // We just load a prepared HTML page from the assets folder for this // sample, see that file for the Javascript implementation webView . loadUrl ( "file:///android_asset/ptr_webview2_sample.html" ) ; }


Input:
Called when the activity is first created . [CODESPLIT] @ Override public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_ptr_list ) ; mPullRefreshListView = ( PullToRefreshListView ) findViewById ( R . id . pull_refresh_list ) ; // Set a listener to be invoked when the list should be refreshed. mPullRefreshListView . setOnRefreshListener ( new OnRefreshListener < ListView > ( ) { @ Override public void onRefresh ( PullToRefreshBase < ListView > refreshView ) { String label = DateUtils . formatDateTime ( getApplicationContext ( ) , System . currentTimeMillis ( ) , DateUtils . FORMAT_SHOW_TIME | DateUtils . FORMAT_SHOW_DATE | DateUtils . FORMAT_ABBREV_ALL ) ; // Update the LastUpdatedLabel refreshView . getLoadingLayoutProxy ( ) . setLastUpdatedLabel ( label ) ; // Do work to refresh the list here. new GetDataTask ( ) . execute ( ) ; } } ) ; // Add an end-of-list listener mPullRefreshListView . setOnLastItemVisibleListener ( new OnLastItemVisibleListener ( ) { @ Override public void onLastItemVisible ( ) { Toast . makeText ( PullToRefreshListActivity . this , "End of List!" , Toast . LENGTH_SHORT ) . show ( ) ; } } ) ; ListView actualListView = mPullRefreshListView . getRefreshableView ( ) ; // Need to use the Actual ListView when registering for Context Menu registerForContextMenu ( actualListView ) ; mListItems = new LinkedList < String > ( ) ; mListItems . addAll ( Arrays . asList ( mStrings ) ) ; mAdapter = new ArrayAdapter < String > ( this , android . R . layout . simple_list_item_1 , mListItems ) ; /**
		 * Add Sound Event Listener
		 */ SoundPullEventListener < ListView > soundListener = new SoundPullEventListener < ListView > ( this ) ; soundListener . addSoundEvent ( State . PULL_TO_REFRESH , R . raw . pull_event ) ; soundListener . addSoundEvent ( State . RESET , R . raw . reset_sound ) ; soundListener . addSoundEvent ( State . REFRESHING , R . raw . refreshing_sound ) ; mPullRefreshListView . setOnPullEventListener ( soundListener ) ; // You can also just use setListAdapter(mAdapter) or // mPullRefreshListView.setAdapter(mAdapter) actualListView . setAdapter ( mAdapter ) ; }


Input: Called when the activity is first created . [CODESPLIT] @ Override public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_ptr_webview ) ; mPullRefreshWebView = ( PullToRefreshWebView ) findViewById ( R . id . pull_refresh_webview ) ; mWebView = mPullRefreshWebView . getRefreshableView ( ) ; mWebView . getSettings ( ) . setJavaScriptEnabled ( true ) ; mWebView . setWebViewClient ( new SampleWebViewClient ( ) ) ; mWebView . loadUrl ( "http://www.google.com" ) ; }


Input: Called when the activity is first created . [CODESPLIT] @ Override public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_ptr_scrollview ) ; mPullRefreshScrollView = ( PullToRefreshScrollView ) findViewById ( R . id . pull_refresh_scrollview ) ; mPullRefreshScrollView . setOnRefreshListener ( new OnRefreshListener < ScrollView > ( ) { @ Override public void onRefresh ( PullToRefreshBase < ScrollView > refreshView ) { new GetDataTask ( ) . execute ( ) ; } } ) ; mScrollView = mPullRefreshScrollView . getRefreshableView ( ) ; }


Input: Helper method for Overscrolling that encapsulates all of the necessary function . <p / > This should only be used on AdapterView s such as ListView as it just calls through to overScrollBy () with the scrollRange = 0 . AdapterView s do not have a scroll range ( i . e . getScrollY () doesn t work ) . [CODESPLIT] public static void overScrollBy ( final PullToRefreshBase < ? > view , final int deltaX , final int scrollX , final int deltaY , final int scrollY , final boolean isTouchEvent ) { overScrollBy ( view , deltaX , scrollX , deltaY , scrollY , 0 , isTouchEvent ) ; }


Input: Helper method for Overscrolling that encapsulates all of the necessary function . This is the advanced version of the call . [CODESPLIT] public static void overScrollBy ( final PullToRefreshBase < ? > view , final int deltaX , final int scrollX , final int deltaY , final int scrollY , final int scrollRange , final int fuzzyThreshold , final float scaleFactor , final boolean isTouchEvent ) { final int deltaValue , currentScrollValue , scrollValue ; switch ( view . getPullToRefreshScrollDirection ( ) ) { case HORIZONTAL : deltaValue = deltaX ; scrollValue = scrollX ; currentScrollValue = view . getScrollX ( ) ; break ; case VERTICAL : default : deltaValue = deltaY ; scrollValue = scrollY ; currentScrollValue = view . getScrollY ( ) ; break ; } // Check that OverScroll is enabled and that we're not currently // refreshing. if ( view . isPullToRefreshOverScrollEnabled ( ) && ! view . isRefreshing ( ) ) { final Mode mode = view . getMode ( ) ; // Check that Pull-to-Refresh is enabled, and the event isn't from // touch if ( mode . permitsPullToRefresh ( ) && ! isTouchEvent && deltaValue != 0 ) { final int newScrollValue = ( deltaValue + scrollValue ) ; if ( PullToRefreshBase . DEBUG ) { Log . d ( LOG_TAG , "OverScroll. DeltaX: " + deltaX + ", ScrollX: " + scrollX + ", DeltaY: " + deltaY + ", ScrollY: " + scrollY + ", NewY: " + newScrollValue + ", ScrollRange: " + scrollRange + ", CurrentScroll: " + currentScrollValue ) ; } if ( newScrollValue < ( 0 - fuzzyThreshold ) ) { // Check the mode supports the overscroll direction, and // then move scroll if ( mode . showHeaderLoadingLayout ( ) ) { // If we're currently at zero, we're about to start // overscrolling, so change the state if ( currentScrollValue == 0 ) { view . setState ( State . OVERSCROLLING ) ; } view . setHeaderScroll ( ( int ) ( scaleFactor * ( currentScrollValue + newScrollValue ) ) ) ; } } else if ( newScrollValue > ( scrollRange + fuzzyThreshold ) ) { // Check the mode supports the overscroll direction, and // then move scroll if ( mode . showFooterLoadingLayout ( ) ) { // If we're currently at zero, we're about to start // overscrolling, so change the state if ( currentScrollValue == 0 ) { view . setState ( State . OVERSCROLLING ) ; } view . setHeaderScroll ( ( int ) ( scaleFactor * ( currentScrollValue + newScrollValue - scrollRange ) ) ) ; } } else if ( Math . abs ( newScrollValue ) <= fuzzyThreshold || Math . abs ( newScrollValue - scrollRange ) <= fuzzyThreshold ) { // Means we've stopped overscrolling, so scroll back to 0 view . setState ( State . RESET ) ; } } else if ( isTouchEvent && State . OVERSCROLLING == view . getState ( ) ) { // This condition means that we were overscrolling from a fling, // but the user has touched the View and is now overscrolling // from touch instead. We need to just reset. view . setState ( State . RESET ) ; } } }


Input: Called when the activity is first created . [CODESPLIT] @ Override public void onCreate ( Bundle savedInstanceState ) { super . onCreate ( savedInstanceState ) ; setContentView ( R . layout . activity_ptr_grid ) ; mPullRefreshGridView = ( PullToRefreshGridView ) findViewById ( R . id . pull_refresh_grid ) ; mGridView = mPullRefreshGridView . getRefreshableView ( ) ; // Set a listener to be invoked when the list should be refreshed. mPullRefreshGridView . setOnRefreshListener ( new OnRefreshListener2 < GridView > ( ) { @ Override public void onPullDownToRefresh ( PullToRefreshBase < GridView > refreshView ) { Toast . makeText ( PullToRefreshGridActivity . this , "Pull Down!" , Toast . LENGTH_SHORT ) . show ( ) ; new GetDataTask ( ) . execute ( ) ; } @ Override public void onPullUpToRefresh ( PullToRefreshBase < GridView > refreshView ) { Toast . makeText ( PullToRefreshGridActivity . this , "Pull Up!" , Toast . LENGTH_SHORT ) . show ( ) ; new GetDataTask ( ) . execute ( ) ; } } ) ; mListItems = new LinkedList < String > ( ) ; TextView tv = new TextView ( this ) ; tv . setGravity ( Gravity . CENTER ) ; tv . setText ( "Empty View, Pull Down/Up to Add Items" ) ; mPullRefreshGridView . setEmptyView ( tv ) ; mAdapter = new ArrayAdapter < String > ( this , android . R . layout . simple_list_item_1 , mListItems ) ; mGridView . setAdapter ( mAdapter ) ; }


Input: Sets the Empty View to be used by the Adapter View . <p / > We need it handle it ourselves so that we can Pull - to - Refresh when the Empty View is shown . <p / > Please note you do <strong > not< / strong > usually need to call this method yourself . Calling setEmptyView on the AdapterView will automatically call this method and set everything up . This includes when the Android Framework automatically sets the Empty View based on it s ID . [CODESPLIT] public final void setEmptyView ( View newEmptyView ) { FrameLayout refreshableViewWrapper = getRefreshableViewWrapper ( ) ; if ( null != newEmptyView ) { // New view needs to be clickable so that Android recognizes it as a // target for Touch Events newEmptyView . setClickable ( true ) ; ViewParent newEmptyViewParent = newEmptyView . getParent ( ) ; if ( null != newEmptyViewParent && newEmptyViewParent instanceof ViewGroup ) { ( ( ViewGroup ) newEmptyViewParent ) . removeView ( newEmptyView ) ; } // We need to convert any LayoutParams so that it works in our // FrameLayout FrameLayout . LayoutParams lp = convertEmptyViewLayoutParams ( newEmptyView . getLayoutParams ( ) ) ; if ( null != lp ) { refreshableViewWrapper . addView ( newEmptyView , lp ) ; } else { refreshableViewWrapper . addView ( newEmptyView ) ; } } if ( mRefreshableView instanceof EmptyViewMethodAccessor ) { ( ( EmptyViewMethodAccessor ) mRefreshableView ) . setEmptyViewInternal ( newEmptyView ) ; } else { mRefreshableView . setEmptyView ( newEmptyView ) ; } mEmptyView = newEmptyView ; }


Input: Specifies the condition for the Rule . [CODESPLIT] public WhenRuleBuilder < T , U > when ( Predicate < NameValueReferableTypeConvertibleMap < T > > condition ) { return new WhenRuleBuilder <> ( _rule , condition ) ; }


Input: Invokes the current Rule s action and then moves down the chain to the successor if the RuleState of the current Rule is next or the action ( s ) was not executed . [CODESPLIT] @ Override @ SuppressWarnings ( "unchecked" ) public void handleRequest ( Object obj ) { boolean actionResult = _rule . invoke ( ( NameValueReferableMap ) obj ) ; if ( ! actionResult || _rule . getRuleState ( ) == RuleState . NEXT ) { getSuccessor ( ) . ifPresent ( handler -> { _rule . getResult ( ) . ifPresent ( result -> handler . getDelegate ( ) . setResult ( ( Result ) result ) ) ; handler . handleRequest ( obj ) ; } ) ; } }


Input: The method getOne () gets the value of the single Fact in the FactMap . [CODESPLIT] @ Override public T getOne ( ) { if ( _facts . size ( ) == 1 ) { return _facts . values ( ) . iterator ( ) . next ( ) . getValue ( ) ; } return null ; }


Input: The method getValue () returns the value of the Fact associated with the name passed in . [CODESPLIT] @ Override public T getValue ( String name ) { return Optional . ofNullable ( _facts . get ( name ) ) . map ( NameValueReferable :: getValue ) . orElse ( null ) ; }


Input: Method getStrVal () gets the String value of a Fact in the FactMap . [CODESPLIT] @ Deprecated public String getStrVal ( String name ) { if ( getValue ( name ) instanceof String ) { return ( String ) getValue ( name ) ; } return String . valueOf ( getValue ( name ) ) ; }


Input: Method getIntVal () gets the Integer value of a Fact in the FactMap . [CODESPLIT] @ Deprecated public Integer getIntVal ( String name ) { Object value = getValue ( name ) ; if ( value != null ) { if ( Integer . class == value . getClass ( ) ) { return ( Integer ) value ; } if ( value . getClass ( ) == String . class ) { return Integer . valueOf ( ( String ) value ) ; } } return null ; }


Input: Method getDblVal () gets the Double value of a Fact in the FactMap . [CODESPLIT] @ Deprecated public Double getDblVal ( String name ) { Object value = getValue ( name ) ; if ( value != null ) { if ( Float . class == value . getClass ( ) ) { return Double . valueOf ( ( Float ) value ) ; } if ( Double . class == value . getClass ( ) ) { return ( Double ) value ; } if ( Integer . class == value . getClass ( ) ) { return Double . valueOf ( ( Integer ) value ) ; } if ( Long . class == value . getClass ( ) ) { return Double . valueOf ( ( Long ) value ) ; } if ( String . class == value . getClass ( ) ) { return Double . parseDouble ( ( String ) value ) ; } } return null ; }


Input: The method setValue sets the value of the Fact by its name . <br / > If no Fact exists with the associated name a new fact is created with the specified name and value . <br / > [CODESPLIT] public void setValue ( String name , T value ) { NameValueReferable < T > fact = _facts . get ( name ) ; if ( fact == null ) { fact = new Fact <> ( name , value ) ; _facts . put ( name , fact ) ; return ; } fact . setValue ( value ) ; }


Input: This put () method is a convenience method for adding a Fact to a FactMap . <br / > It uses the name of the Fact as the key and the Fact as the value . [CODESPLIT] @ Override public Fact < T > put ( NameValueReferable < T > fact ) { return put ( fact . getName ( ) , fact ) ; }


Input: Adds a fact to the Rule . [CODESPLIT] GivenRuleBuilder < T , U > given ( String name , T value ) { return given ( new Fact < T > ( name , value ) ) ; }


Input: Adds one or more facts into the Rule . [CODESPLIT] @ SafeVarargs public final GivenRuleBuilder < T , U > given ( NameValueReferable ... facts ) { _rule . addFacts ( facts ) ; return this ; }


Input: Adds a using constraint in the Rule that restricts the facts supplied to the subsequent then action . [CODESPLIT] public UsingRuleBuilder < T , U > using ( String ... factNames ) { return new UsingRuleBuilder < T , U > ( _rule , factNames ) ; }


Input: Returns a new RuleBuilder for the specified Rule class . [CODESPLIT] public static RuleBuilder < Object , Object > create ( Class < ? extends Rule > ruleClass , RuleChainActionType actionType ) { return new RuleBuilder <> ( ruleClass , actionType ) ; }


Input: Returns a new RuleBuilder for the default Rule type . [CODESPLIT] public static RuleBuilder < Object , Object > create ( ) { RuleBuilder < Object , Object > rule = new RuleBuilder <> ( GoldenRule . class ) ; rule . _factType = Object . class ; return rule ; }


Input: Specifies the fact type for the Rule being built . [CODESPLIT] public < S > RuleBuilder < S , U > withFactType ( Class < S > factType ) { RuleBuilder < S , U > builder = new RuleBuilder <> ( _ruleClass ) ; builder . _factType = factType ; builder . _resultType = _resultType ; builder . _actionType = _actionType ; return builder ; }


Input: Specifies the Result type for the Rule being built . [CODESPLIT] public < S > RuleBuilder < T , S > withResultType ( Class < S > resultType ) { RuleBuilder < T , S > builder = new RuleBuilder <> ( _ruleClass ) ; builder . _factType = _factType ; builder . _resultType = resultType ; builder . _actionType = _actionType ; builder . _name = _name ; return builder ; }


Input: Adds a fact to the Rule using a name value pair to specify a new fact . [CODESPLIT] @ SuppressWarnings ( "unchecked" ) public GivenRuleBuilder < T , U > given ( String name , T value ) { Rule < T , U > rule = _name . map ( ruleName -> ( Rule < T , U > ) new AuditableRule < T , U > ( newRule ( ) , ruleName ) ) . orElse ( newRule ( ) ) ; if ( rule == null ) { throw new IllegalStateException ( "No Rule is instantiated; An invalid Rule class may have been provided" ) ; } return new GivenRuleBuilder < T , U > ( rule , new Fact < T > ( name , value ) ) ; }


Input: Adds one or more facts to the Rule . [CODESPLIT] @ SuppressWarnings ( "unchecked" ) @ SafeVarargs public final GivenRuleBuilder < T , U > given ( NameValueReferable ... facts ) { Rule < T , U > rule = _name . map ( name -> ( Rule < T , U > ) new AuditableRule < T , U > ( newRule ( ) , name ) ) . orElse ( newRule ( ) ) ; if ( rule == null ) { throw new IllegalStateException ( "No Rule is instantiated; An invalid Rule class may have been provided" ) ; } return new GivenRuleBuilder < T , U > ( rule , facts ) ; }


Input: Specifies the condition for the Rule . [CODESPLIT] @ SuppressWarnings ( "unchecked" ) public WhenRuleBuilder < T , U > when ( Predicate < NameValueReferableTypeConvertibleMap < T > > condition ) { Rule < T , U > rule = _name . map ( name -> ( Rule < T , U > ) new AuditableRule < T , U > ( newRule ( ) , name ) ) . orElse ( newRule ( ) ) ; if ( rule == null ) { throw new IllegalStateException ( "No Rule is instantiated; An invalid Rule class may have been provided" ) ; } return new WhenRuleBuilder < T , U > ( rule , condition ) ; }


Input: Adds a using constraint in the Rule that restricts the facts supplied to the subsequent then action . [CODESPLIT] @ SuppressWarnings ( "unchecked" ) public UsingRuleBuilder < T , U > using ( String ... factNames ) { Rule < T , U > rule = _name . map ( name -> ( Rule < T , U > ) new AuditableRule < T , U > ( newRule ( ) , name ) ) . orElse ( newRule ( ) ) ; if ( rule == null ) { throw new IllegalStateException ( "No Rule is instantiated; An invalid Rule class may have been provided" ) ; } return new UsingRuleBuilder < T , U > ( rule , factNames ) ; }


Input: Adds an action as a Consumer to the Rule . [CODESPLIT] @ SuppressWarnings ( "unchecked" ) public ThenRuleBuilder < T , U > then ( Consumer < NameValueReferableTypeConvertibleMap < T > > action ) { Rule < T , U > rule = _name . map ( name -> ( Rule < T , U > ) new AuditableRule < T , U > ( newRule ( ) , name ) ) . orElse ( newRule ( ) ) ; if ( rule == null ) { throw new IllegalStateException ( "No Rule is instantiated; An invalid Rule class may have been provided" ) ; } return new ThenRuleBuilder < T , U > ( rule , action ) ; }


Input: Convert the Facts to properties with the [CODESPLIT] @ SuppressWarnings ( "unchecked" ) private void mapFactsToProperties ( NameValueReferableMap facts ) { for ( Field field : getAnnotatedFields ( Given . class , _pojoRule . getClass ( ) ) ) { Given given = field . getAnnotation ( Given . class ) ; try { field . setAccessible ( true ) ; if ( NameValueReferable . class . isAssignableFrom ( field . getType ( ) ) ) { field . set ( _pojoRule , facts . get ( given . value ( ) ) ) ; } else { Object value = facts . getValue ( given . value ( ) ) ; if ( value != null ) { //set the field to the Fact that has the name of the @Given value field . set ( _pojoRule , value ) ; } else if ( NameValueReferableMap . class . isAssignableFrom ( field . getType ( ) ) ) { //if the field is a FactMap then give it the FactMap field . set ( _pojoRule , facts ) ; } else if ( Collection . class . isAssignableFrom ( field . getType ( ) ) ) { //set a Collection of Fact object values Stream stream = facts . values ( ) . stream ( ) . filter ( fact -> { //filter on only facts that contain objects matching the generic type ParameterizedType paramType = ( ParameterizedType ) field . getGenericType ( ) ; Class < ? > genericType = ( Class < ? > ) paramType . getActualTypeArguments ( ) [ 0 ] ; return genericType . equals ( ( ( NameValueReferable ) fact ) . getValue ( ) . getClass ( ) ) ; } ) . map ( fact -> { ParameterizedType paramType = ( ParameterizedType ) field . getGenericType ( ) ; Class < ? > genericType = ( Class < ? > ) paramType . getActualTypeArguments ( ) [ 0 ] ; return genericType . cast ( ( ( NameValueReferable ) fact ) . getValue ( ) ) ; } ) ; if ( List . class == field . getType ( ) ) { //map List of Fact values to field field . set ( _pojoRule , stream . collect ( Collectors . toList ( ) ) ) ; } else if ( Set . class == field . getType ( ) ) { //map Set of Fact values to field field . set ( _pojoRule , stream . collect ( Collectors . toSet ( ) ) ) ; } } else if ( Map . class == field . getType ( ) ) { //map Map of Fact values to field Map map = ( Map ) facts . keySet ( ) . stream ( ) . filter ( key -> { ParameterizedType paramType = ( ParameterizedType ) field . getGenericType ( ) ; Class < ? > genericType = ( Class < ? > ) paramType . getActualTypeArguments ( ) [ 1 ] ; return genericType . equals ( facts . getValue ( ( String ) key ) . getClass ( ) ) ; } ) . collect ( Collectors . toMap ( key -> key , key -> facts . getValue ( ( String ) key ) ) ) ; field . set ( _pojoRule , map ) ; } } } catch ( Exception ex ) { LOGGER . error ( "Unable to update field '" + field . getName ( ) + "' in rule object '" + _pojoRule . getClass ( ) + "'" , ex ) ; } } }


Input: Returns a rule instance [CODESPLIT] protected Object getRuleInstance ( Class < ? > rule ) { try { return rule . newInstance ( ) ; } catch ( InstantiationException | IllegalAccessException ex ) { LOGGER . warn ( "Unable to create instance of rule using '" + rule + "'" , ex ) ; } return null ; }


Input: The run () method adds the rules [ via defineRules () ] and runs the rules as long as at least one rule was added . [CODESPLIT] public final void run ( ) { if ( _headRule . isPresent ( ) == false ) { defineRules ( ) ; } _headRule . ifPresent ( rule -> rule . given ( _facts ) ) ; _headRule . ifPresent ( Rule :: run ) ; }


Input: The given () method accepts the Facts for this RuleBook . The facts passed in will also be applied to all rules added to this RuleBook . [CODESPLIT] @ SafeVarargs public final RuleBook < T > given ( Fact < T > ... facts ) { Arrays . stream ( facts ) . forEach ( _facts :: add ) ; return this ; }


Input: The given () method accepts a key / value pair as a Fact for this RuleBook . [CODESPLIT] public RuleBook < T > given ( String name , T value ) { _facts . add ( new Fact < T > ( name , value ) ) ; return this ; }


Input: The addRule () method adds a rule to the end of the Rules chain . [CODESPLIT] public void addRule ( Rule < T > rule ) { if ( rule == null ) { return ; } if ( ! _headRule . isPresent ( ) ) { _headRule = Optional . of ( rule ) ; // this rule is the head if there was no head _tailRule = rule ; } else { _tailRule . setNextRule ( rule ) ; _tailRule = rule ; } }


Input: Method getAnnotatedFields gets the fields annotated of a specific type from the class and its parent classes . <br / > The List is in order of closest parent = > current obj fields parent obj fields etc . [CODESPLIT] @ SuppressWarnings ( "unchecked" ) public static List < Field > getAnnotatedFields ( Class annotation , Class clazz ) { if ( clazz == Object . class ) { return new ArrayList <> ( ) ; } List < Field > fields = ( List < Field > ) Arrays . stream ( clazz . getDeclaredFields ( ) ) . filter ( field -> field . getAnnotation ( annotation ) != null ) . collect ( Collectors . toList ( ) ) ; if ( clazz . getSuperclass ( ) != null ) { fields . addAll ( getAnnotatedFields ( annotation , clazz . getSuperclass ( ) ) ) ; } return fields ; }


Input: Method getAnnotatedField gets the first annotated field of the type of annotation specified . [CODESPLIT] public static Optional < Field > getAnnotatedField ( Class annotation , Class clazz ) { List < Field > fields = getAnnotatedFields ( annotation , clazz ) ; return Optional . ofNullable ( fields . size ( ) > 0 ? fields . get ( 0 ) : null ) ; }


Input: Method getAnnotatedMethods gets the methods annotated of a specific type from the class and its parent classes . <br / > The List is in order of closest parent = > current obj methods parent obj methods etc . [CODESPLIT] @ SuppressWarnings ( "unchecked" ) public static List < Method > getAnnotatedMethods ( Class annotation , Class clazz ) { List < Method > methods = new ArrayList <> ( ) ; if ( clazz == Object . class ) { return methods ; } methods . addAll ( ( List < Method > ) Arrays . stream ( clazz . getDeclaredMethods ( ) ) . filter ( field -> field . getAnnotation ( annotation ) != null ) . collect ( Collectors . toList ( ) ) ) ; methods . addAll ( getAnnotatedMethods ( annotation , clazz . getSuperclass ( ) ) ) ; return methods ; }


Input: Method getAnnotatedMethod the first annotated method of the type of annotation specified . [CODESPLIT] public static Optional < Method > getAnnotatedMethod ( Class annotation , Class clazz ) { List < Method > methods = getAnnotatedMethods ( annotation , clazz ) ; return Optional . ofNullable ( methods . size ( ) > 0 ? methods . get ( 0 ) : null ) ; }


Input: Method getAnnotation returns the annotation on a class or its parent annotation . [CODESPLIT] @ SuppressWarnings ( "unchecked" ) public static < A extends Annotation > A getAnnotation ( Class < A > annotation , Class < ? > clazz ) { return Optional . ofNullable ( clazz . getAnnotation ( annotation ) ) . orElse ( ( A ) Arrays . stream ( clazz . getDeclaredAnnotations ( ) ) . flatMap ( anno -> Arrays . stream ( anno . getClass ( ) . getInterfaces ( ) ) . flatMap ( iface -> Arrays . stream ( iface . getDeclaredAnnotations ( ) ) ) ) . filter ( annotation :: isInstance ) . findFirst ( ) . orElse ( null ) ) ; }


Input: This create () method is a convenience method to avoid using new and generic syntax . [CODESPLIT] public static < T , U > StandardDecision < T , U > create ( Class < T > factType , Class < U > resultType ) { return new StandardDecision < T , U > ( factType , resultType ) ; }


Input: The given () method accepts a name / value pair to be used as a Fact . [CODESPLIT] @ Override public Decision < T , U > given ( String name , T value ) { _rule . given ( name , value ) ; return this ; }


Input: The given () method accepts Facts for the StandardDecision . [CODESPLIT] @ Override public Decision < T , U > given ( Fact < T > ... facts ) { _rule . given ( facts ) ; return this ; }


Input: The given () method accepts Facts for the StandardDecision . [CODESPLIT] @ Override public Decision < T , U > given ( FactMap < T > facts ) { _rule . given ( facts ) ; return this ; }


Input: The when () method accepts a { [CODESPLIT] @ Override public Decision < T , U > when ( Predicate < FactMap < T > > test ) { _rule . when ( test ) ; return this ; }