com.codebutler.farebot.transit.orca.OrcaTransitFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.codebutler.farebot.transit.orca.OrcaTransitFactory.java

Source

/*
 * OrcaTransitFactory.java
 *
 * This file is part of FareBot.
 * Learn more at: https://codebutler.github.io/farebot/
 *
 * Copyright (C) 2014-2016 Eric Butler <eric@codebutler.com>
 * Copyright (C) 2015 Sean CyberKitsune McClenaghan <cyberkitsune09@gmail.com>
 *
 * Thanks to:
 * Karl Koscher <supersat@cs.washington.edu>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.codebutler.farebot.transit.orca;

import android.support.annotation.NonNull;

import com.codebutler.farebot.card.desfire.DesfireCard;
import com.codebutler.farebot.card.desfire.DesfireFile;
import com.codebutler.farebot.card.desfire.RecordDesfireFile;
import com.codebutler.farebot.core.ByteUtils;
import com.codebutler.farebot.transit.TransitFactory;
import com.codebutler.farebot.transit.TransitIdentity;
import com.codebutler.farebot.transit.Trip;

import org.apache.commons.lang3.ArrayUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static com.codebutler.farebot.transit.orca.OrcaData.TRANS_TYPE_CANCEL_TRIP;
import static com.codebutler.farebot.transit.orca.OrcaData.TRANS_TYPE_TAP_IN;
import static com.codebutler.farebot.transit.orca.OrcaData.TRANS_TYPE_TAP_OUT;

public class OrcaTransitFactory implements TransitFactory<DesfireCard, OrcaTransitInfo> {

    @Override
    public boolean check(@NonNull DesfireCard card) {
        return (card.getApplication(0x3010f2) != null);
    }

    @NonNull
    @Override
    public TransitIdentity parseIdentity(@NonNull DesfireCard card) {
        try {
            byte[] data = card.getApplication(0xffffff).getFile(0x0f).getData().bytes();
            return TransitIdentity.create("ORCA", String.valueOf(ByteUtils.byteArrayToInt(data, 4, 4)));
        } catch (Exception ex) {
            throw new RuntimeException("Error parsing ORCA serial", ex);
        }
    }

    @NonNull
    @Override
    public OrcaTransitInfo parseInfo(@NonNull DesfireCard card) {
        byte[] data;
        int serialNumber;
        int balance;
        List<Trip> trips;

        try {
            data = card.getApplication(0xffffff).getFile(0x0f).getData().bytes();
            serialNumber = ByteUtils.byteArrayToInt(data, 5, 3);
        } catch (Exception ex) {
            throw new RuntimeException("Error parsing ORCA serial", ex);
        }

        try {
            data = card.getApplication(0x3010f2).getFile(0x04).getData().bytes();
            balance = ByteUtils.byteArrayToInt(data, 41, 2);
        } catch (Exception ex) {
            throw new RuntimeException("Error parsing ORCA balance", ex);
        }

        try {
            trips = parseTrips(card);
        } catch (Exception ex) {
            throw new RuntimeException("Error parsing ORCA trips", ex);
        }

        return new AutoValue_OrcaTransitInfo(trips, serialNumber, balance);
    }

    @NonNull
    private static List<Trip> parseTrips(@NonNull DesfireCard card) {
        List<Trip> trips = new ArrayList<>();

        DesfireFile file = card.getApplication(0x3010f2).getFile(0x02);
        if (file instanceof RecordDesfireFile) {
            RecordDesfireFile recordFile = (RecordDesfireFile) card.getApplication(0x3010f2).getFile(0x02);

            OrcaTrip[] useLog = new OrcaTrip[recordFile.getRecords().size()];
            for (int i = 0; i < useLog.length; i++) {
                useLog[i] = OrcaTrip.create(recordFile.getRecords().get(i));
            }
            Arrays.sort(useLog, new Trip.Comparator());
            ArrayUtils.reverse(useLog);

            for (int i = 0; i < useLog.length; i++) {
                OrcaTrip trip = useLog[i];
                OrcaTrip nextTrip = (i + 1 < useLog.length) ? useLog[i + 1] : null;

                if (isSameTrip(trip, nextTrip)) {
                    trips.add(MergedOrcaTrip.create(trip, nextTrip));
                    i++;
                    continue;
                }

                trips.add(trip);
            }
        }
        Collections.sort(trips, new Trip.Comparator());
        return trips;
    }

    private static boolean isSameTrip(@NonNull OrcaTrip firstTrip, @NonNull OrcaTrip secondTrip) {
        return firstTrip != null && secondTrip != null && firstTrip.getTransType() == TRANS_TYPE_TAP_IN
                && (secondTrip.getTransType() == TRANS_TYPE_TAP_OUT
                        || secondTrip.getTransType() == TRANS_TYPE_CANCEL_TRIP)
                && firstTrip.getAgency() == secondTrip.getAgency();
    }

}