MovieDB.java

/*
 * Copyright © 2011 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. 
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners. 
 * See LICENSE.TXT for license information.
 */
package com.nokia.example.moviebooking.moviedb;

import java.util.Vector;
import java.util.Hashtable;
import java.util.Calendar;
import java.util.Random;
import java.util.Date;
import java.io.InputStream;
import java.io.IOException;

/**
 * MovieDB: Gets access to the local/remote movie database For this
 * example we use a simple file to store the data A more elaborated
 * example would probably connect to a remote server
 */
public class MovieDB {

    private MovieDBListener listener;
    private int moviesCount = 3;
    private Vector movies = new Vector();
    private Hashtable props;

    public MovieDB(InputStream in) {
        props = new Hashtable();
        try {
            StringBuffer sb = new StringBuffer();
            String key = null;
            String value = null;
            int chr = 0;
            while (chr != -1) {
                chr = in.read();
                if (chr == '=') {
                    key = new String(sb).trim();
                    sb = new StringBuffer();
                } else if (key != null && (chr == '\n' || chr == -1)) {
                    value = new String(sb).trim();
                    sb = new StringBuffer();
                    props.put(key, value);
                    key = null;
                } else {
                    sb.append((char) chr);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // set the listener
    public void setDBListener(MovieDBListener listener) {
        this.listener = listener;
    }

    // request MovieDB to get the data
    public void loadData() {
        // Create the database in a separate thread
        new Thread(new DBGenerator()).start();
    }

    // return a list of movies
    public Vector getMovies() {
        return movies;
    }

    public void close() {
        // do nothing. In a real case this should close any
        // DB or network connection
    }

    // request a Booking
    public void sendBookingRequest(String firstNameText,
            String familyNameText, Showing showing,
            int[][] selectedSeats) {
        // Simulate a request in a separate
        // thread
        new Thread(new ReservationRequest(selectedSeats, showing)).start();
    }

    // Sends an event to listeners
    private void sendEvent(int event, Object data) {
        if (listener != null) {
            listener.handleEvent(new MovieDBEvent(event, data));
        }
    }

    private final class DBGenerator implements Runnable {
        // use a set of fixed times for Showings

        private final int[] SHOWING_TIMES = new int[]{13, 15, 19, 22, 23};

        public void run() {
            try {
                synchronized (this) {
                    sendEvent(MovieDBEvent.CONNECTING, "Local database..");
                    //if (props != null) {
                    if (props != null) {
                        // Simulate a connection delay
                        wait(500);
                        moviesCount = Integer.parseInt((String) props.get("count"));
                        // Inform how many movies are there
                        sendEvent(MovieDBEvent.MOVIES_COUNT,
                                new Integer(moviesCount));
                        Random r = new Random();
                        Date now = new Date();
                        for (int i = 0; i < moviesCount; i++) {
                            // Small delay per movie
                            wait(1000);

                            Vector showings = new Vector();
                            // generate showings for 3 days from now
                            final int DAYS_OF_SHOWINGS = 3;
                            for (int k = 0; k < DAYS_OF_SHOWINGS; k++) {
                                // and for each hour in SHOWING_TIMES
                                for (int j = 0; j < SHOWING_TIMES.length; j++) {
                                    Calendar cal = Calendar.getInstance();
                                    cal.set(Calendar.HOUR_OF_DAY, 0);
                                    cal.set(Calendar.MINUTE, 0);
                                    cal.set(Calendar.SECOND, 0);
                                    cal.set(Calendar.MILLISECOND, 0);
                                    cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) + k);
                                    try {
                                        // If this fails, this month doesn't have so many days
                                        cal.getTime();
                                    } catch (IllegalArgumentException e) {
                                        //  Move to the next month
                                        if (cal.get(Calendar.MONTH) == Calendar.DECEMBER) {
                                            cal.set(Calendar.MONTH, Calendar.JANUARY);
                                        } else {
                                            cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + 1);
                                        }
                                        // Continue from the beginning of the next month.
                                        cal.set(Calendar.DAY_OF_MONTH, DAYS_OF_SHOWINGS - k);
                                    }
                                    cal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY) + SHOWING_TIMES[j]);
                                    // only add showings after now
                                    if (cal.getTime().getTime() > now.getTime()) {
                                        // the teather is a bit boring, always 10x15
                                        int rows = 10;
                                        int seatsPerRow = 15;
                                        boolean[][] seating = new boolean[rows][seatsPerRow];

                                        // Generate the seating
                                        for (int l = 0; l < rows; l++) {
                                            for (int m = 0; m < seatsPerRow; m++) {
                                                // one third of seats is occupied
                                                seating[l][m] = (Math.abs(r.nextInt() % 3) == 0);
                                            }
                                        }
                                        // give it some random price
                                        Showing show = new Showing(cal.getTime(), Math.abs(r.nextInt() % 100) * 10, seating);
                                        showings.addElement(show);
                                    }
                                }
                            }
                            // Create movie based on the property file
                            String movieKey = "movie" + i;

                            Movie movie = new Movie((String) props.get(movieKey + ".name"),
                                    (String) props.get(movieKey + ".imageLocation"),
                                    (String) props.get(movieKey + ".description"),
                                    showings);
                            movies.addElement(movie);
                            sendEvent(MovieDBEvent.MOVIE_LOADED, new Integer(i));
                        }
                        sendEvent(MovieDBEvent.DATA_LOADED, null);
                    } else {
                        sendEvent(MovieDBEvent.DATA_ERROR, "No database found");
                    }
                }
            } catch (InterruptedException e) {
                // Ignore, this shouldn't happen
            }
        }
    }

    // Internal class used to simulate a booking request in a separate
    // thread
    private final class ReservationRequest implements Runnable {

        private final int[][] seats;
        private final Showing showing;
        private final Random rnd = new Random();

        private ReservationRequest(int[][] seats, Showing showing) {
            this.seats = seats;
            this.showing = showing;
        }

        public void run() {
            try {
                synchronized (this) {
                    sendEvent(MovieDBEvent.RESERVATION_REQUESTED, null);
                    // add a small delay
                    wait(1000);
                    // the booking is always approved so generate the price
                    // and an id code
                    double price = showing.getPrice() * seats.length;
                    int code = Math.abs(rnd.nextInt());

                    BookingTicket ticket = new BookingTicket(price, code);
                    sendEvent(MovieDBEvent.MOVIE_RESERVED, ticket);
                }
            } catch (InterruptedException e) {
                // just ignore
            }
        }
    }
}