Java tutorial
/** * Copyright 2014 Galois, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.galois.qrstream.qrpipe; import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Stopwatch; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; /** * Stores message from sequence of decoded QR codes. Note, the initial * capacity is unknown until first QR code is read. */ public final class DecodedMessage { // Container for saving received data. // Using SortedMap so that message can be assembled in order. private final SortedMap<Integer, PartialMessage> receivedData; // Track progress of decoding private final IProgress decodeProgress; private DecodeState decodeState; // Performance metric counters private int numRepeatedQRDecodes = 0; private final Logger logger = LoggerFactory.getLogger(Log.LOG_NAME); private final Logger perfLog = LoggerFactory.getLogger(Log.TIMING_LOG); private final Stopwatch stopwatch = Stopwatch.createUnstarted(); public DecodedMessage(IProgress progress) { // Initialize 'decodeState' upon decoding first QR code. receivedData = new TreeMap<Integer, PartialMessage>(); decodeProgress = progress; } /** * Returns true when whole message has been received, * otherwise it returns false. */ public boolean isComplete() { return (decodeState != null && (decodeState.getState() == State.Final)); } /** * Returns the whole transmitted message whenever it is available, otherwise * it returns an empty message to indicate only partial message received. */ public byte[] getEntireMessage() { if (receivedData.isEmpty() || (decodeState.getState() != State.Final)) { return new byte[0]; } // Assemble message in order, we assume key are sorted ByteArrayDataOutput bstream = ByteStreams.newDataOutput(); for (Entry<Integer, PartialMessage> entry : receivedData.entrySet()) { bstream.write(entry.getValue().getPayload()); } return bstream.toByteArray(); } /** * Mark transmission failure. Expect no more QR codes to decode. */ protected void setFailedDecoding() { DecodeState failed; // Possible for transmission to fail before decodeState is initialized. if (decodeState == null) { failed = new DecodeState(1); } else { failed = decodeState; } failed.markFailedTransmission(); decodeProgress.changeState(failed); } /** * Mark progress of data transmission by setting the {@code chunkId} * bit in {@code DecodeState} to true whenever a QR code has been decoded. * It also sets up the initial sizes of {@code receivedData} if this is * the first QR code encountered. * @return The {@code State} indicating whether the whole message has been received. */ protected State saveMessageChunk(PartialMessage msgPart) { if (msgPart == null) { return State.Fail; } // Set up message container if this is the first QR code encountered. if (decodeState == null) { // This is the beginning of the message. Start the performance timer! stopwatch.start(); decodeState = new DecodeState(msgPart.getTotalChunks()); receivedData.clear(); } // Save message part if we haven't seen it already. if (!receivedData.containsKey(msgPart.getChunkId())) { receivedData.put(msgPart.getChunkId(), msgPart); decodeState.markDataReceived(msgPart.getChunkId()); // Only update progress indicator when decoding is successful // and we haven't seen this part of the message before. decodeProgress.changeState(decodeState); logger.debug("QRLib: Saving chunk " + msgPart.getChunkId() + " of " + msgPart.getTotalChunks()); } else { logger.debug("QRLib: Already saved chunk " + msgPart.getChunkId() + " of " + msgPart.getTotalChunks()); numRepeatedQRDecodes++; } State currentState = decodeState.getState(); if (currentState == State.Final) { // This is the end of the message. Report the decoding performance. stopwatch.stop(); long secToDecodeMessage = stopwatch.elapsed(TimeUnit.SECONDS); perfLog.debug("Time (in seconds) to decode message after receiving a " + "valid QR code: " + secToDecodeMessage); } return currentState; } protected void logNumberDuplicateQRDecodes() { perfLog.debug("Number of duplicate QR decodes: " + numRepeatedQRDecodes); } }