55import java .util .ArrayList ;
66import java .util .List ;
77import java .util .Map ;
8+ import java .util .Map .Entry ;
89import java .util .function .Function ;
910
1011import javax .validation .constraints .NotNull ;
1516import com .aerospike .client .Bin ;
1617import com .aerospike .client .IAerospikeClient ;
1718import com .aerospike .client .Key ;
19+ import com .aerospike .client .Operation ;
1820import com .aerospike .client .Record ;
1921import com .aerospike .client .Value ;
22+ import com .aerospike .client .cdt .ListOperation ;
23+ import com .aerospike .client .cdt .ListReturnType ;
24+ import com .aerospike .client .cdt .MapOperation ;
25+ import com .aerospike .client .cdt .MapOrder ;
26+ import com .aerospike .client .cdt .MapPolicy ;
27+ import com .aerospike .client .cdt .MapReturnType ;
2028import com .aerospike .client .policy .BatchPolicy ;
2129import com .aerospike .client .policy .Policy ;
2230import com .aerospike .client .policy .QueryPolicy ;
2533import com .aerospike .client .policy .WritePolicy ;
2634import com .aerospike .client .query .RecordSet ;
2735import com .aerospike .client .query .Statement ;
36+ import com .aerospike .mapper .annotations .AerospikeEmbed ;
37+ import com .aerospike .mapper .annotations .AerospikeEmbed .EmbedType ;
2838import com .aerospike .mapper .tools .ClassCache .PolicyType ;
39+ import com .aerospike .mapper .tools .TypeUtils .AnnotatedType ;
2940import com .aerospike .mapper .tools .configuration .ClassConfig ;
3041import com .aerospike .mapper .tools .configuration .Configuration ;
42+ import com .aerospike .mapper .tools .mappers .ListMapper ;
3143import com .fasterxml .jackson .core .JsonParseException ;
3244import com .fasterxml .jackson .core .JsonProcessingException ;
3345import com .fasterxml .jackson .databind .JsonMappingException ;
@@ -335,7 +347,107 @@ public boolean delete(WritePolicy writePolicy, @NotNull Object object) throws Ae
335347 }
336348 return mClient .delete (writePolicy , key );
337349 }
350+
351+ public <T > VirtualList <T > asBackedList (@ NotNull Object object , @ NotNull String binName , Class <T > clazz ) {
352+ return new VirtualList <T >(this , object , binName , clazz );
353+ }
354+
355+ public static class VirtualList <E > {
356+ private final AeroMapper mapper ;
357+ private final ValueType value ;
358+ private final ClassCacheEntry <?> owningEntry ;
359+ private final ClassCacheEntry <?> elementEntry ;
360+ private final String binName ;
361+ private final ListMapper listMapper ;
362+ private final Key key ;
363+ private final EmbedType listType ;
364+ private final EmbedType elementType ;
365+
366+ public VirtualList (@ NotNull AeroMapper mapper , @ NotNull Object object , @ NotNull String binName , @ NotNull Class <E > clazz ) {
367+ Class <?> owningClazz = object .getClass ();
368+ this .owningEntry = (ClassCacheEntry <?>) ClassCache .getInstance ().loadClass (owningClazz , mapper );
369+ this .elementEntry = (ClassCacheEntry <?>) ClassCache .getInstance ().loadClass (clazz , mapper );
370+ this .mapper = mapper ;
371+ this .binName = binName ;
372+ this .value = owningEntry .getValueFromBinName (binName );
373+ if (value == null ) {
374+ throw new AerospikeException (String .format ("Class %s has no bin called %s" , clazz .getSimpleName (), binName ));
375+ }
376+ String set = owningEntry .getSetName ();
377+ if ("" .equals (set )) {
378+ // Use the null set
379+ set = null ;
380+ }
381+ key = new Key (owningEntry .getNamespace (), set , Value .get (owningEntry .getKey (object )));
382+
383+ AnnotatedType annotatedType = value .getAnnotatedType ();
384+ AerospikeEmbed embed = annotatedType .getAnnotation (AerospikeEmbed .class );
385+ if (embed == null ) {
386+ throw new AerospikeException (String .format ("Bin %s on class %s is not specified as a embedded" , binName , clazz .getSimpleName ()));
387+ }
388+ listType = embed .type () == EmbedType .DEFAULT ? EmbedType .LIST : embed .type ();
389+ elementType = embed .elementType () == EmbedType .DEFAULT ? EmbedType .MAP : embed .elementType ();
390+ Class <?> binClazz = value .getType ();
391+ if (!(binClazz .isArray () || (Map .class .isAssignableFrom (binClazz )) || List .class .isAssignableFrom (binClazz ))) {
392+ throw new AerospikeException (String .format ("Bin %s on class %s is not a collection class" , binName , clazz .getSimpleName ()));
393+ }
394+
395+ TypeMapper typeMapper = value .getTypeMapper ();
396+ if (typeMapper instanceof ListMapper ) {
397+ listMapper = ((ListMapper )typeMapper );
398+ }
399+ else {
400+ throw new AerospikeException (String .format ("Bin %s on class %s is not mapped via a listMapper. This is unexpected" , binName , clazz .getSimpleName ()));
401+ }
402+ }
403+
404+ public VirtualList <E > keptInSync (boolean inSync ) {
405+ return this ;
406+ }
407+
338408
409+ private Operation getAppendOperation (Object aerospikeObject ) {
410+ if (aerospikeObject instanceof Entry ) {
411+ Entry <Object , Object > entry = (Entry ) aerospikeObject ;
412+ return MapOperation .put (new MapPolicy (MapOrder .KEY_ORDERED , 0 ), binName , Value .get (entry .getKey ()), Value .get (entry .getValue ()));
413+ }
414+ else {
415+ return ListOperation .append (binName , Value .get (aerospikeObject ));
416+ }
417+ }
418+ public VirtualList <E > append (E element ) {
419+ return this .append (null , element );
420+ }
421+
422+ public VirtualList <E > append (WritePolicy writePolicy , E element ) {
423+ Object result = listMapper .toAerospikeInstanceFormat (element );
424+ if (writePolicy == null ) {
425+ writePolicy = new WritePolicy (owningEntry .getWritePolicy ());
426+ writePolicy .recordExistsAction = RecordExistsAction .UPDATE ;
427+ }
428+ this .mapper .mClient .operate (writePolicy , key , getAppendOperation (result ));
429+ return this ;
430+ }
431+
432+ public E get (int index ) {
433+ // TODO
434+ //Object aerospikeKey = elementEntry.translateKeyToAerospikeKey(key);
435+ Operation operation ;
436+ if (listType == EmbedType .LIST ) {
437+ operation = ListOperation .getByIndex (binName , index , ListReturnType .VALUE );
438+ }
439+ else {
440+ operation = MapOperation .getByIndex (binName , index , MapReturnType .KEY_VALUE );
441+ }
442+ Record record = this .mapper .mClient .operate (null , key , operation );
443+ List <Object > list = (List <Object >) record .getList (binName );
444+ Object result = listMapper .fromAerospikeInstanceFormat (list .get (0 ));
445+
446+ return (E ) result ;
447+ }
448+ }
449+
450+
339451 public <T > void find (@ NotNull Class <T > clazz , Function <T , Boolean > function ) throws AerospikeException {
340452 ClassCacheEntry <T > entry = getEntryAndValidateNamespace (clazz );
341453
@@ -362,7 +474,6 @@ public <T> void find(@NotNull Class<T> clazz, Function<T, Boolean> function) thr
362474 recordSet .close ();
363475 }
364476 }
365-
366477 }
367478
368479 // --------------------------------------------------------------------------------------------------
0 commit comments