11/*
2- * Copyright © 2016, 2017 IBM Corp. All rights reserved.
2+ * Copyright © 2016, 2018 IBM Corp. All rights reserved.
33 *
44 * Original iOS version by Jens Alfke, ported to Android by Marty Schoch
55 * Copyright © 2012 Couchbase, Inc. All rights reserved.
3838import com .cloudant .sync .event .notifications .DocumentDeleted ;
3939import com .cloudant .sync .event .notifications .DocumentModified ;
4040import com .cloudant .sync .event .notifications .DocumentUpdated ;
41+ import com .cloudant .sync .internal .common .CouchConstants ;
4142import com .cloudant .sync .internal .common .CouchUtils ;
4243import com .cloudant .sync .internal .common .ValueListMap ;
4344import com .cloudant .sync .internal .documentstore .callables .ChangesCallable ;
@@ -242,9 +243,16 @@ public InternalDocumentRevision read(final String id, final String rev) throws
242243 DocumentNotFoundException , DocumentStoreException {
243244 Misc .checkState (this .isOpen (), "Database is closed" );
244245 Misc .checkNotNullOrEmpty (id , "Document id" );
245-
246246 try {
247- return get (queue .submit (new GetDocumentCallable (id , rev , this .attachmentsDir , this .attachmentStreamFactory )));
247+ if (id .startsWith (CouchConstants ._local_prefix )) {
248+ Misc .checkArgument (rev == null , "Local documents must have a null revision ID" );
249+ String localId = id .substring (CouchConstants ._local_prefix .length ());
250+ LocalDocument ld = get (queue .submit (new GetLocalDocumentCallable (localId )));
251+ // convert to DocumentRevision, adding back "_local/" prefix which was stripped off when document was written
252+ return new DocumentRevisionBuilder ().setDocId (CouchConstants ._local_prefix + ld .docId ).setBody (ld .body ).build ();
253+ } else {
254+ return get (queue .submit (new GetDocumentCallable (id , rev , this .attachmentsDir , this .attachmentStreamFactory )));
255+ }
248256 } catch (ExecutionException e ) {
249257 throwCauseAs (e , DocumentNotFoundException .class );
250258 String message = String .format (Locale .ENGLISH , "Failed to get document id %s at revision %s" , id , rev );
@@ -879,13 +887,28 @@ public DocumentRevision create(final DocumentRevision rev)
879887 Misc .checkArgument (rev .isFullRevision (), "Projected revisions cannot be used to " +
880888 "create documents" );
881889 final String docId ;
890+
882891 // create docid if docid is null
883892 if (rev .getId () == null ) {
884893 docId = CouchUtils .generateDocumentId ();
885894 } else {
886895 docId = rev .getId ();
887896 }
888897
898+ // check to see if we are creating a local (non-replicating) document
899+ if (docId .startsWith (CouchConstants ._local_prefix )) {
900+ String localId = docId .substring (CouchConstants ._local_prefix .length ());
901+ try {
902+ insertLocalDocument (localId , rev .getBody ());
903+ // we can return the input document as-is since there was no doc id or rev id to generate
904+ return rev ;
905+ } catch (DocumentException e ) {
906+ throw new DocumentStoreException (e .getMessage (), e .getCause ());
907+ } finally {
908+ eventBus .post (new DocumentCreated (rev ));
909+ }
910+ }
911+
889912 // We need to work out which of the attachments for the revision are ones
890913 // we can copy over because they exist in the attachment store already and
891914 // which are new, that we need to prepare for insertion.
@@ -942,6 +965,11 @@ public DocumentRevision update(final DocumentRevision rev)
942965 Misc .checkArgument (rev .isFullRevision (), "Projected revisions cannot be used to " +
943966 "create documents" );
944967
968+ // Shortcut if this is a create/update of local doc
969+ if (rev .getId ().startsWith (CouchConstants ._local_prefix )) {
970+ return create (rev );
971+ }
972+
945973 // Shortcut if this is a deletion
946974 if (rev .isDeleted ()) {
947975 return delete (rev );
@@ -993,17 +1021,28 @@ public DocumentRevision delete(final DocumentRevision rev) throws
9931021 ConflictException , DocumentNotFoundException , DocumentStoreException {
9941022 Misc .checkNotNull (rev , "DocumentRevision" );
9951023 Misc .checkState (isOpen (), "Datastore is closed" );
996-
9971024 try {
998- InternalDocumentRevision deletedRevision = get (queue .submit (new DeleteDocumentCallable (rev .getId (), rev .getRevision ())));
999- if (deletedRevision != null ) {
1000- eventBus .post (new DocumentDeleted (rev , deletedRevision ));
1025+ // local documents
1026+ if (rev .getId ().startsWith (CouchConstants ._local_prefix )) {
1027+ Misc .checkArgument (rev .getRevision () == null , "Local documents must have a null revision ID" );
1028+ String localId = rev .getId ().substring (CouchConstants ._local_prefix .length ());
1029+ deleteLocalDocument (localId );
1030+ // for local documents there is no "new document" to post on the event bus or return as
1031+ // the document is removed rather than updated with a tombstone
1032+ eventBus .post (new DocumentDeleted (rev , null ));
1033+ return null ;
1034+ } else {
1035+ // "normal" documents
1036+ InternalDocumentRevision deletedRevision = get (queue .submit (new DeleteDocumentCallable (rev .getId (), rev .getRevision ())));
1037+ if (deletedRevision != null ) {
1038+ eventBus .post (new DocumentDeleted (rev , deletedRevision ));
1039+ }
1040+ return deletedRevision ;
10011041 }
1002- return deletedRevision ;
10031042 } catch (ExecutionException e ) {
10041043 // conflictexception if source revision isn't current rev
10051044 throwCauseAs (e , ConflictException .class );
1006- // documentnotfoundexception if it's already deleted
1045+ // documentnotfoundexception if it's already deleted, or was a non-existent local document
10071046 throwCauseAs (e , DocumentNotFoundException .class );
10081047 String message = "Failed to delete document" ;
10091048 logger .log (Level .SEVERE , message , e );
@@ -1014,11 +1053,21 @@ public DocumentRevision delete(final DocumentRevision rev) throws
10141053 // delete all leaf nodes
10151054 @ Override
10161055 public List <DocumentRevision > delete (final String id )
1017- throws DocumentStoreException {
1056+ throws DocumentNotFoundException , DocumentStoreException {
10181057 Misc .checkNotNull (id , "ID" );
10191058 try {
1020- return get (queue .submitTransaction (new DeleteAllRevisionsCallable (id )));
1059+ if (id .startsWith (CouchConstants ._local_prefix )) {
1060+ String localId = id .substring (CouchConstants ._local_prefix .length ());
1061+ deleteLocalDocument (localId );
1062+ // for local documents there is no "new document" to return as the document is
1063+ // removed rather than updated with a tombstone
1064+ return Collections .singletonList (null );
1065+ } else {
1066+ return get (queue .submitTransaction (new DeleteAllRevisionsCallable (id )));
1067+ }
10211068 } catch (ExecutionException e ) {
1069+ // documentnotfoundexception if it was a non-existent local document
1070+ throwCauseAs (e , DocumentNotFoundException .class );
10221071 String message = "Failed to delete document" ;
10231072 logger .log (Level .SEVERE , message , e );
10241073 throw new DocumentStoreException (message , e .getCause ());
@@ -1085,4 +1134,4 @@ public void createWithHistory(DocumentRevision revision, int revisionsStart, Lis
10851134 forceInsert (Collections .singletonList (new ForceInsertItem (internalRev ,
10861135 revIDs , null , preparedAttachments , false )));
10871136 }
1088- }
1137+ }
0 commit comments