private boolean save(Document document, boolean deletion, ConcurrencyControl concurrencyControl)
        throws CouchbaseLiteException {
        if (deletion && !document.exists()) {
            throw new CouchbaseLiteException("Cannot delete a document that has not yet been saved.",
                CBLError.Domain.CBLITE, CBLError.Code.NOT_FOUND);
        }

        C4Document curDoc = null;
        C4Document newDoc = null;
        synchronized (lock) {
            mustBeOpen();
            prepareDocument(document);
            boolean commit = false;
            beginTransaction();
            try {
                try {
                    newDoc = save(document, null, deletion);
                    commit = true;
                }
                catch (LiteCoreException e) {
                    if (!(e.domain == C4Constants.ErrorDomain.LITE_CORE
                        && e.code == C4Constants.LiteCoreError.CONFLICT)) {
                        throw CBLStatus.convertException(e);
                    }
                }

                if (newDoc == null) {
                    // Handle conflict:
                    if (concurrencyControl.equals(ConcurrencyControl.FAIL_ON_CONFLICT)) {
                        return false; // document is conflicted and return false because of OPTIMISTIC
                    }

                    try {
                        curDoc = getC4Database().get(document.getId(), true);
                    }
                    catch (LiteCoreException e) {
                        if (deletion
                            && e.domain == C4Constants.ErrorDomain.LITE_CORE
                            && e.code == C4Constants.LiteCoreError.NOT_FOUND) {
                            return true;
                        }
                        else { throw CBLStatus.convertException(e); }
                    }

                    if (deletion && curDoc.deleted()) {
                        document.replaceC4Document(curDoc);
                        curDoc = null; // NOTE: prevent to call curDoc.free() in finally block
                        return true;
                    }

                    // Save changes on the current branch:
                    // NOTE: curDoc null check is done in prev try-catch blcok
                    try {
                        newDoc = save(document, curDoc, deletion);
                    }
                    catch (LiteCoreException e) {
                        throw CBLStatus.convertException(e);
                    }
                }

                document.replaceC4Document(newDoc);
                commit = true;
            }
            finally {
                if (curDoc != null) {
                    curDoc.retain();
                    curDoc.release(); // curDoc is not retained
                }
                try {
                    endTransaction(commit); // true: commit the transaction, false: abort the transaction
                }
                catch (CouchbaseLiteException e) {
                    if (newDoc != null) {
                        newDoc.release(); // newDoc is already retained
                    }
                    throw e;
                }
            }
            return true;
        }
    }
private boolean save ( Document document , boolean deletion , ConcurrencyControl concurrencyControl ) throws CouchbaseLiteException { if ( deletion && ! document . exists ( ) ) { throw new CouchbaseLiteException ( "Cannot delete a document that has not yet been saved." , CBLError . Domain . CBLITE , CBLError . Code . NOT_FOUND ) ; } C4Document curDoc = null ; C4Document newDoc = null ; synchronized ( lock ) { mustBeOpen ( ) ; prepareDocument ( document ) ; boolean commit = false ; beginTransaction ( ) ; try { try { newDoc = save ( document , null , deletion ) ; commit = true ; } catch ( LiteCoreException e ) { if ( ! ( e . domain == C4Constants . ErrorDomain . LITE_CORE && e . code == C4Constants . LiteCoreError . CONFLICT ) ) { throw CBLStatus . convertException ( e ) ; } } if ( newDoc == null ) { if ( concurrencyControl . equals ( ConcurrencyControl . FAIL_ON_CONFLICT ) ) { return false ; } try { curDoc = getC4Database ( ) . get ( document . getId ( ) , true ) ; } catch ( LiteCoreException e ) { if ( deletion && e . domain == C4Constants . ErrorDomain . LITE_CORE && e . code == C4Constants . LiteCoreError . NOT_FOUND ) { return true ; } else { throw CBLStatus . convertException ( e ) ; } } if ( deletion && curDoc . deleted ( ) ) { document . replaceC4Document ( curDoc ) ; curDoc = null ; return true ; } try { newDoc = save ( document , curDoc , deletion ) ; } catch ( LiteCoreException e ) { throw CBLStatus . convertException ( e ) ; } } document . replaceC4Document ( newDoc ) ; commit = true ; } finally { if ( curDoc != null ) { curDoc . retain ( ) ; curDoc . release ( ) ; } try { endTransaction ( commit ) ; } catch ( CouchbaseLiteException e ) { if ( newDoc != null ) { newDoc . release ( ) ; } throw e ; } } return true ; } }
The main save method.