aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/thedevstack/conversationsplus/services/filetransfer/FileTransferEntity.java
blob: e1b40fa6226d5286de8c3b3e403e48263021cbdd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package de.thedevstack.conversationsplus.services.filetransfer;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import de.thedevstack.conversationsplus.entities.DownloadableFile;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.entities.Transferable;
import de.thedevstack.conversationsplus.persistance.FileBackend;
import de.thedevstack.conversationsplus.utils.StreamUtil;

/**
 *
 */
public class FileTransferEntity implements Transferable {
    /**
     * Listeners to inform about a status change.
     */
    private final List<FileTransferStatusListener> statusListeners = new ArrayList<>();
    /**
     * The associated message.
     */
    private final Message message;
    /**
     * Number of attempts.
     */
    private int attempts = 0;
    /**
     * The status of the file transfer.
     */
    private FileTransferStatusEnum transferStatus;
    /**
     * Number of bytes transmitted.
     */
    private long transmitted = 0;
    /**
     * The associated file.
     */
    private DownloadableFile file;
    /**
     * The associated file as stream.
     */
    private InputStream fileInputStream;

    /**
     * Initializes the FileTransferEntity based on the associated message.
     * This initialization includes loading the file and associating this transferable to the message.
     * @param message the message in which the file to transfer is contained.
     */
    public FileTransferEntity(Message message) {
        this.message = message;
        this.message.setTransferable(this);
        this.file = FileBackend.getFile(message, false);
    }

    /**
     * Start something.
     * Empty implementation since documentation in interface is missing.
     * @return <code>false</code>
     */
    @Override
    public boolean start() {
        return false;
    }

    /**
     * Returns the global transferable status.
     *
     * @return {@value STATUS_FAILED} if #isFailed returns <code>true</code>, {@value STATUS_UPLOADING} otherwise
     */
    @Override
    public int getStatus() {
        int status = (isFailed()) ? STATUS_FAILED : STATUS_UPLOADING;
        return status;
    }

    /**
     * Returns the expected file size of the underlying file.
     * @return the expected file size or 0 if no file is associated.
     */
    @Override
    public long getFileSize() {
        return file == null ? 0 : file.getExpectedSize();
    }

    /**
     * Calculates the current progress in percent.
     *
     * @return the current progress in percent
     */
    @Override
    public int getProgress() {
        if (file == null) {
            return 0;
        }
        return (int) ((((double) transmitted) / file.getExpectedSize()) * 100);
    }

    /**
     * Cancels the file transfer and informs the listeners about cancellation.
     */
    @Override
    public void cancel() {
        this.transferStatus = FileTransferStatusEnum.CANCELED;

        this.close();

        for (FileTransferStatusListener listener : this.statusListeners) {
            listener.onCancel(this);
        }
    }

    /**
     * Starts an transfer attempt.
     */
    public void startAttempt() {
        this.attempts++;
        this.transferStatus = FileTransferStatusEnum.TRANSFERRING;
    }

    /**
     * Fails an file transfer and informs the listeners about failure.
     *
     * @param failureReason the reason of failure.
     */
    public void fail(FileTransferFailureReason failureReason) {
        this.transferStatus = FileTransferStatusEnum.FAILED;

        this.close();

        failureReason.setAttempt(this.attempts);
        for (FileTransferStatusListener listener : this.statusListeners) {
            listener.onFailure(this, failureReason);
        }
    }

    /**
     * Updates the progress by adding parameter value to current progress value.
     *
     * @param progress the number of newly transferred bytes
     */
    public void updateProgress(long progress) {
        if (0 == this.attempts) {
            this.startAttempt();
        }
        this.transmitted += progress;
    }

    /**
     * Set the status of the file transfer to FileTransferStatusEnum#TRANSFERRED and informs the listeners about success.
     */
    public void transferred() {
        this.transferStatus = FileTransferStatusEnum.TRANSFERRED;

        this.close();

        for (FileTransferStatusListener listener : this.statusListeners) {
            listener.onSuccess(this);
        }
    }

    /**
     * Closes the file input stream (if it is not yet closed) and removes association with message.
     */
    private void close() {
        StreamUtil.close(this.fileInputStream);
        this.getMessage().setTransferable(null);
    }

    /**
     * Whether the file is transferred or not.
     * @return <code>true</code> if the file is successfully transferred, <code>false</code> otherwise
     */
    public boolean isTransferred() {
        return FileTransferStatusEnum.TRANSFERRED == this.transferStatus;
    }

    /**
     * Whether the file transfer is canceled or not.
     * @return <code>true</code> if the file transfer was canceled, <code>false</code> otherwise
     */
    public boolean isCanceled() {
        return FileTransferStatusEnum.CANCELED == this.transferStatus;
    }

    /**
     * Whether the file transfer failed or not.
     * @return <code>true</code> if the file transfer failed, <code>false</code> otherwise
     */
    public boolean isFailed() {
        return FileTransferStatusEnum.FAILED == this.transferStatus;
    }

    public void setFileInputStream(InputStream fileInputStream) {
        this.fileInputStream = fileInputStream;
    }

    public InputStream getFileInputStream() {
        return fileInputStream;
    }

    public Message getMessage() {
        return message;
    }

    public DownloadableFile getFile() {
        return file;
    }

    public void addFileTransferStatusListener(FileTransferStatusListener... listeners) {
        this.statusListeners.addAll(Arrays.asList(listeners));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        FileTransferEntity that = (FileTransferEntity) o;

        String uuid = message != null ? message.getUuid() : null;
        String thatUuid = that.message != null ? that.message.getUuid() : null;

        return uuid != null ? uuid.equals(thatUuid) : thatUuid == null;

    }

    @Override
    public int hashCode() {
        return message != null && message.getUuid() != null ? message.getUuid().hashCode() : 0;
    }
}