net.yatomiya.nicherry.services.bbs.ThreadUpdateHandler.java Source code

Java tutorial

Introduction

Here is the source code for net.yatomiya.nicherry.services.bbs.ThreadUpdateHandler.java

Source

/*******************************************************************************
 * Copyright (c) 2014,2015 Hideki Yatomi
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 ******************************************************************************/
package net.yatomiya.nicherry.services.bbs;

import java.io.*;
import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import com.squareup.okhttp.*;
import net.yatomiya.e4.ui.dialogs.*;
import net.yatomiya.e4.util.*;
import net.yatomiya.nicherry.*;
import net.yatomiya.nicherry.model.bbs.*;
import net.yatomiya.nicherry.services.bbs.model.*;

public class ThreadUpdateHandler extends ModelUpdateHandler {
    boolean isDeltaGetSuccess;
    byte[] contents;
    byte[] sjisDat;
    String downloadedDatText;
    List<MMessage> downloadedMessages;

    ThreadUpdateHandler() {
        super();

        isDeltaGetSuccess = false;
        contents = new byte[0];
        sjisDat = new byte[0];
    }

    @Override
    public ThreadProcessor getProcessor() {
        return (ThreadProcessor) super.getProcessor();
    }

    @Override
    public MThread getModel() {
        return (MThread) super.getModel();
    }

    @Override
    public ThreadUpdateEvent getUpdateEvent() {
        return (ThreadUpdateEvent) super.getUpdateEvent();
    }

    @Override
    protected UpdateEvent createUpdateEvent() {
        return new ThreadUpdateEvent();
    }

    @Override
    protected String createRequestUrl() {
        MBoard board = getProcessor().getManager().getBBSService().getBoard(getModel().getId().getBoardId());
        if (board == null)
            return "";
        return board.getUrl() + "dat/" + getModel().getDatKey() + ".dat";
    }

    Response processIntercept(Interceptor.Chain chain) throws IOException {
        Response response = null;
        Request.Builder builder = chain.request().newBuilder();

        if (!isForceUpdate()) {
            // try delta get.
            byte[] tail = getProcessor().getDatTail().getTail();
            if (tail.length > 0) {
                File datFile = getProcessor().getDatFile();
                if (IOUtils.isFileExists(datFile)) {
                    long datLen = datFile.length();
                    if (datLen > 0) {
                        long getStartOffset = datLen - tail.length;
                        if (getStartOffset > 0) {
                            builder.header("Range", "bytes=" + getStartOffset + "-");

                            // Disable gzip compression. Gzip compression and partial get can't co-exist.
                            // Header with empty value must be set. If header is not set, HttpClient default implementation
                            // adds header with value accepting gzip format automatically.
                            builder.header("Accept-Encoding", "");

                            response = chain.proceed(builder.build());

                            switch (response.code()) {
                            case 200: // OK
                            case 206: // Partial Content
                                // check contents.
                                contents = response.body().bytes();

                                if (contents.length < tail.length
                                        || !Arrays.equals(tail, Arrays.copyOfRange(contents, 0, tail.length))) {
                                    // Tail data of local dat is different from server, we must get whole dat.
                                    // That means local dat is NOT identical to dat in server, something is modified in server.
                                    // So we must get whole dat from server again.
                                    // If region except stored in tail is modified in server, and length of dat in server equals to dat in local,
                                    // we can't find out those differences.

                                    contents = null;
                                } else {
                                    // delta contents is valid.
                                    isDeltaGetSuccess = true;
                                    return response;
                                }
                                break;
                            case 304: // Not Modified
                                // Not modified, so no need to update.
                                return response;
                            case 416: // Range Not Satisfied
                            default:
                                // Failed.
                                break;
                            }
                        }
                    }
                }
            }
        }

        // do get all
        return chain.proceed(chain.request());
    }

    @Override
    protected boolean handleValidateResponse(Response response) {
        if (isDeltaGetSuccess && response.code() == 206) {
            return true;
        }
        return super.handleValidateResponse(response);
    }

    @Override
    protected boolean handleBackground(Response response) throws IOException {
        int noOffset;
        if (isDeltaGetSuccess) {
            // content is already loaded when delta get.
            sjisDat = Arrays.copyOfRange(contents, getProcessor().getDatTail().getTail().length, contents.length);
            noOffset = getModel().getMessages().size();
        } else {
            contents = response.body().bytes();
            sjisDat = contents;
            noOffset = 0;
        }

        downloadedDatText = StringUtils.getString(sjisDat, NConstants.CHARSET_SHIFT_JIS);
        downloadedMessages = new DatParser().parse(getModel(), downloadedDatText, noOffset);

        if (sjisDat.length <= 0) {
            // http code is not 'Not modified', but no data was sent in response body.
            getUpdateEvent().setType(UpdateEvent.Type.NOT_MODIFIED);
            return false;
        }

        return true;
    }

    @Override
    protected boolean handleForeground() throws IOException {
        MThread thread = getModel();

        List<MMessage> msgs = getModel().getMessages();

        boolean wholeChanged = false;

        // ????????? dat ?????????
        if (!isDeltaGetSuccess) {
            if (msgs.size() > 0) {
                int matchedCount = 0;
                for (int i = 0; i < msgs.size(); i++) {
                    if (i < downloadedMessages.size()
                            && BBSUtils.messageEquals(msgs.get(i), downloadedMessages.get(i))) {
                        matchedCount++;
                    }
                }

                if (matchedCount == 0)
                    wholeChanged = true;

                /*
                if (matchedCount != msgs.size()) {
                boolean result = askIfOverrideDat(getModel(), downloadedDatText);
                if (!result)
                    return false;
                }
                */
            }
        }

        if (wholeChanged) {
            getModel().getPostMessageRecordList().clear();
        }

        getProcessor().getDatTail().set(sjisDat);

        IOUtils.write(getProcessor().getDatFile(), sjisDat, isDeltaGetSuccess);

        List<MMessage> oldMessages = new ArrayList<>(msgs);
        if (!isDeltaGetSuccess)
            msgs.clear();
        msgs.addAll(downloadedMessages);

        int lastCount = thread.getMessageCount();
        if (wholeChanged)
            lastCount = 0;
        thread.setLastMessageCount(lastCount);
        thread.setMessageCount(msgs.size());

        // Get thread title from message No.1.
        if (JUtils.isEmpty(thread.getName()) && msgs.size() > 0) {
            thread.setName(msgs.get(0).getExtText());
        }

        if (isDeltaGetSuccess) {
            AnalyzedThread at = getProcessor().getAnalyzedThread();
            at.addMessages(downloadedMessages);
        } else {
            getProcessor().resetAnalyzedStates();
        }

        getProcessor().checkIfPostMessageIsUpdatedInDat();

        ThreadUpdateEvent event = getUpdateEvent();
        event.setDeltaGet(isDeltaGetSuccess);
        event.setOldMessages(oldMessages);
        event.setDownloadedMessages(downloadedMessages);

        return true;
    }

    @Override
    protected String getFinishedAnnounceMessage() {
        return "";
    }

    private boolean askIfOverrideDat(MThread thread, String newDatText) {
        TextMessageDialog dlg = new TextMessageDialog(null, TextMessageDialog.Type.WARNING,
                String.format("dat ??? : %s", BBSUtils.getPathName(thread)),
                "? dat ???? dat ?????\n"
                        + "?????????\n" + "\n" + newDatText,
                SWT.READ_ONLY | SWT.H_SCROLL | SWT.V_SCROLL) {
            @Override
            protected Point getInitialSize() {
                return new Point(600, 400);
            }
        };
        int result = dlg.open();
        return result == TextMessageDialog.OK;
    }
}