Package tvbrowserdataservice.file

Source Code of tvbrowserdataservice.file.SummaryFile$ChannelFrame

/*
* TV-Browser
* Copyright (C) 04-2003 Martin Oberhauser (darras@users.sourceforge.net)
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
* CVS information:
*  $RCSfile$
*   $Source$
*     $Date: 2010-06-28 19:33:48 +0200 (Mon, 28 Jun 2010) $
*   $Author: bananeweizen $
* $Revision: 6662 $
*/
package tvbrowserdataservice.file;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.zip.GZIPOutputStream;

import util.io.DownloadJob;
import util.io.FileFormatException;
import util.io.IOUtilities;
import devplugin.Date;

/**
* The summary file gives an overview over the files located on a mirror and
* their versions.
*
* @author Til Schneider, www.murfman.de
*/
public class SummaryFile extends AbstractFile {

  /** The default file name for a summary file. */
  public static final String SUMMARY_FILE_NAME = "summary.gz";
  /** The version of the file format. */
  private static final int FILE_VERSION = 1;
  /** The charset used to encode Strings. */
  private static final String TEXT_CHARSET = "UTF-8";
 
  /**
   * The hash containings the ChannelFrames.
   * (key = String (See {@link #getChannelKey(String, String)}),
   *  value = ChannelFrame)
   */
  private HashMap<String, ChannelFrame> mChannelFrameHash;
  /** The number of levels used in this summary. */
  private int mLevelCount;
 
 
  /**
   * Creates a new instance.
   */
  public SummaryFile() {
    mChannelFrameHash = new HashMap<String, ChannelFrame>();
    mLevelCount = 0;
  }
 
 
  /**
   * Sets the version of a day program.
   *
   * @param date The date of the day program.
   * @param country The country of the day program's channel.
   * @param channelId The ID of the day program's channel.
   * @param level The level to set the version for.
   * @param version The version to set.
   */
  public void setDayProgramVersion(Date date, String country, String channelId,
    int level, int version)
  {
    if (version < 0) {
      // Unknown version
      version = 0;
    }
   
    // Update level count
    if (level >= mLevelCount) {
      mLevelCount = (level + 1);
    }
   
    // Try to get a frame from the hash
    String key = getChannelKey(country, channelId);
    ChannelFrame frame = mChannelFrameHash.get(key);
    if (frame == null) {
      // There is no frame -> create one
      frame = new ChannelFrame(country, channelId);
      System.out.println("creating frame for "+country+", "+channelId);
      mChannelFrameHash.put(key, frame);
    }
   
    // Set the version
    frame.setVersion(date, level, version);
  }
 
 
  /**
   * Gets the version of a day program.
   *
   * @param date The date of the day program.
   * @param country The country of the day program's channel.
   * @param channelId The ID of the day program's channel.
   * @param level The level to get the version for.
   * @return The version of the day program or <code>-1</code> if there is no
   *         version for that day program and level.
   */
  public int getDayProgramVersion(Date date, String country, String channelId,
    int level)
  {
    if (level >= mLevelCount) {
      // We have no data for that level
      return -1;
    }
   
    // Try to get a frame from the hash
    String key = getChannelKey(country, channelId);
    ChannelFrame frame = mChannelFrameHash.get(key);
   
    if (frame == null) {
      // We have no data for that channel
      return -1;
    } else {
      int version = frame.getVersion(date, level);
      if (version == 0) {
        return -1;
      } else {
        return version;
      }
    }
  }

 
  /**
   * Gets the hash key for a day program.
   *
   * @param country The country of the day program's channel.
   * @param channelId The ID of the day program's channel.
   * @return The hash key.
   */
  private String getChannelKey(String country, String channelId) {
    return new StringBuilder(country).append('_').append(channelId).toString();
  }


  /**
   * Reads the summary from a stream.
   *
   * @param stream The stream to read the summary from.
   */
  public void readFromStream(InputStream stream, DownloadJob job)
    throws IOException, FileFormatException
  {
    InputStream gIn = IOUtilities.openSaveGZipInputStream(stream);
   
    // The header
    int fileVersion = gIn.read();
    if (fileVersion > FILE_VERSION) {
      throw new FileFormatException("Unknown file version: " + fileVersion);
    }
   
    int startDaysSince1970 = ((gIn.read() & 0xFF) << 16)
                           | ((gIn.read() & 0xFF) << 8)
                           (gIn.read() & 0xFF);
   
    mLevelCount = gIn.read();

    int frameCount = ((gIn.read() & 0xFF) << 8)
                   (gIn.read() & 0xFF);

    // The frames
    mChannelFrameHash.clear();
    Date startDate = generateDate(startDaysSince1970);
    for (int frameIdx = 0; frameIdx < frameCount; frameIdx++) {
      String country = readString(gIn);
      String channelId = readString(gIn);
      ChannelFrame frame = new ChannelFrame(country, channelId);
      String key = getChannelKey(country, channelId);
      mChannelFrameHash.put(key, frame);
     
     
      int daysCount = gIn.read();
      Date date = startDate;
      for (int i = 0; i < daysCount; i++) {
        byte[] versionArr = IOUtilities.readBinaryData(gIn, mLevelCount);
        frame.setVersionArray(date, versionArr);
        date = date.addDays(1);
      }
    }
   
    gIn.close();
  }

  /**
   * Generates a Date.
   * The Constructor of Date is deprecated, it has some troubles with OS/2, but in this
   * class we only use it to compute the difference between two days. Only for this purpose
   * it's ok.
   *
   * @param daysSince1970 Days since 1970
   * @return Date-Object
   */
  private Date generateDate(int daysSince1970) {
      long l = (long) daysSince1970 * 24 * 60 * 60 * 1000;
      java.util.Date d = new java.util.Date(l);
      Calendar cal = Calendar.getInstance();
      cal.setTime(d);
     
      int year = cal.get(Calendar.YEAR);
      int month = cal.get(Calendar.MONTH) + 1;
      int day = cal.get(Calendar.DAY_OF_MONTH);
     
      return new Date(year, month, day);
  }
 
 
  /**
   * Writes the summary into a stream.
   *
   * @param stream The stream to write the summary to.
   */
  public void writeToStream(OutputStream stream, File fileName)
    throws IOException, FileFormatException
  {
    GZIPOutputStream gOut = new GZIPOutputStream(stream);

   
   
    gOut.write(FILE_VERSION);

    System.out.println("get the minimum start date...");
   
    // Get the minimum start date
    int minStartDaysSince1970 = Integer.MAX_VALUE;
    Iterator<ChannelFrame> iter = mChannelFrameHash.values().iterator();
    while (iter.hasNext()) {
      ChannelFrame frame = iter.next();
      int startDaysSince1970 = frame.getStartDaysSince1970();
      if (startDaysSince1970 < minStartDaysSince1970) {
        minStartDaysSince1970 = startDaysSince1970;
      }
    }
   
    gOut.write((byte) (minStartDaysSince1970 >> 16));
    gOut.write((byte) (minStartDaysSince1970 >> 8));
    gOut.write((byte) (minStartDaysSince1970));
   
    gOut.write((byte) mLevelCount);

    int frameCount = mChannelFrameHash.size();
    gOut.write((byte) (frameCount >> 8));
    gOut.write((byte) (frameCount));

    System.out.println("write frames...");
    System.out.println("frameCount: "+frameCount);
   
    // The frames
    Date startDate = generateDate(minStartDaysSince1970);
    System.out.println("minStartDaysSince1970: "+minStartDaysSince1970);
    iter = mChannelFrameHash.values().iterator();
    while (iter.hasNext()) {
      ChannelFrame frame = iter.next();
     
     
      System.out.println(frame.getChannelId()+", "+frame.getStartDaysSince1970()+", "+frame.getDaysCount(minStartDaysSince1970));
     
      writeString(gOut, frame.getCountry());
      writeString(gOut, frame.getChannelId());
     
      int daysCount = frame.getDaysCount(minStartDaysSince1970);
      gOut.write((byte) daysCount);
     
      Date date = startDate;
      for (int i = 0; i < daysCount; i++) {
        byte[] versionArr = frame.getVersionArray(date);
        gOut.write(versionArr);
        date = date.addDays(1);
      }
    }
       
    gOut.close();
  }


  /**
   * Reads a String from a stream.
   *
   * @param stream The stream to read from.
   * @return The String.
   */
  private String readString(InputStream stream)
    throws IOException, FileFormatException
  {
    int len = stream.read();
    if (len == -1) {
      throw new FileFormatException("Unexpected end of stream");
    }
   
    byte[] data = IOUtilities.readBinaryData(stream, len);
    return new String(data, TEXT_CHARSET);
  }


  /**
   * Writes a String into a stream.
   *
   * @param stream The stream to write to.
   * @param str The String to write.
   */
  private void writeString(OutputStream stream, String str)
    throws IOException, FileFormatException
  {
    byte[] data;
    try {
      data = str.getBytes(TEXT_CHARSET);
    }
    catch (UnsupportedEncodingException exc) {
      throw new FileFormatException("Encoding not supported: " + TEXT_CHARSET, exc);
    }
   
    stream.write((byte) data.length);
    stream.write(data);
  }


  /**
   * Gets the days since 1.1.1970 for the given date.
   *
   * @param date The date to calculate the days since 1970 for.
   * @return The days since 1970.
   */
  private int getDaysSince1970(Date date) {
    Calendar cal = date.getCalendar();
    int zoneOffset = cal.get(Calendar.ZONE_OFFSET);
    int daylight = cal.get(Calendar.DST_OFFSET);
    java.util.Date utilDate = cal.getTime();
    long millis = utilDate.getTime() + zoneOffset + daylight;
    return (int) (millis / 1000L / 60L / 60L / 24L);
  }
 
 
  /**
   * A channel frame of a summary file. Holds all file versions for one channel.
   */
  private class ChannelFrame {
   
    /** The channel's country. */
    private String mCountry;
    /** The channel's ID. */
    private String mChannelId;
    /** The date (in days since 1970) the first version array is for. */
    private int mStartDaysSince1970;
    /**
     * The list of version arrays. Each version array contains the versions
     * for all levels. (<code>versionForLevel1 = versionArr[1]</code>)
     */
    private ArrayList<byte[]> mVersionList;

   
    /**
     * Creates a new instance.
     *
     * @param country The channel's country.
     * @param channelId The channel's ID.
     */
    public ChannelFrame(String country, String channelId) {
      mCountry = country;
      mChannelId = channelId;
    }
   
   
    /**
     * Gets the channel's country.
     *
     * @return The channel's country.
     */
    public String getCountry() {
      return mCountry;
    }
   
   
    /**
     * Gets the channel's ID.
     *
     * @return The channel's ID.
     */
    public String getChannelId() {
      return mChannelId;
    }
   
   
    /**
     * Gets the date (in days since 1970) the first version array is for.
     *
     * @return The date the first version array is for.
     */
    public int getStartDaysSince1970() {
      return mStartDaysSince1970;
    }
   
   
    /**
     * Gets the number of days this frame has versions for since the starting
     * date.
     *
     * @param startDays The day to count from (in days after 1.1.1970)
     * @return The number of days this frame has versions for
     */
    public int getDaysCount(int startDays) {
      // NOTE: mStartDaysSince1970 may be after startDays
      return mVersionList.size() + (mStartDaysSince1970 - startDays);
    }


    /**
     * Gets the version array for a particular date.
     * <p>
     * The returned array has a length of {@link #mLevelCount}.
     *
     * @param date The date to get the version array for.
     * @return The version array for a particular date.
     */
    public byte[] getVersionArray(Date date) {
      // Try to get the version array
      byte[] versionArr = null;
      if (mVersionList != null) {
        int dateDaysSince1970 = getDaysSince1970(date);
        int differenceDays = dateDaysSince1970 - mStartDaysSince1970;
        if ((differenceDays >= 0) && (differenceDays < mVersionList.size())) {
          versionArr = mVersionList.get(differenceDays);
        }
      }
     
      // Verify the version array
      if (versionArr == null) {
        // We have no version array -> Create one
        versionArr = new byte[mLevelCount];
      }
      else if (versionArr.length < mLevelCount) {
        // The current versionArr is too short -> Create a bigger one
        byte[] oldVersionArr = versionArr;
        versionArr = new byte[mLevelCount];
         
        System.arraycopy(oldVersionArr, 0, versionArr, 0, oldVersionArr.length);
      }
     
      return versionArr;
    }
   
   
    /**
     * Sets the version array for a particular date.
     *
     * @param date The date to set the version array for.
     * @param versionArr The version array to set.
     */
    public void setVersionArray(Date date, byte[] versionArr) {
      int dateDaysSince1970 = getDaysSince1970(date);

      // Check whether we already have versions
      if (mVersionList == null) {
        // We have no versions
        mStartDaysSince1970 = dateDaysSince1970;
        mVersionList = new ArrayList<byte[]>();
        mVersionList.add(versionArr);
      } else {
        // We already have versions

        // Check whether the date fits
        int differenceDays = dateDaysSince1970 - mStartDaysSince1970;
        if (differenceDays < 0) {
          // We have to insert empty version arrays
          int daysToInsert = differenceDays * -1;
          for (int i = 0; i < daysToInsert; i++) {
            mVersionList.add(0, null);
          }
           
          mStartDaysSince1970 = dateDaysSince1970;
          differenceDays = 0;
        }
        else if (differenceDays >= mVersionList.size()) {
          // We have to append empty version arrays
          int daysToAppend = differenceDays - mVersionList.size() + 1;
          for (int i = 0; i < daysToAppend; i++) {
            mVersionList.add(null);
          }
        }
 
        // Set the version array
        mVersionList.set(differenceDays, versionArr);
      }
    }


    /**
     * Gets the version for a date and level.
     * <p>
     * If the version is unkown <code>0</code> is returned.
     *
     * @param date The date to get the version for.
     * @param level The level to get the version for.
     * @return The version for a date and level.
     */
    public int getVersion(Date date, int level) {
      // Get the version array
      byte[] versionArr = getVersionArray(date);

      // Get the version
      return versionArr[level];
    }
   

    /**
     * Sets the version for a date and level.
     *
     * @param date The date to set the version for.
     * @param level The level to set the version for.
     * @param version The version to set.
     */
    public void setVersion(Date date, int level, int version) {
      // Get the version array
      byte[] versionArr = getVersionArray(date);

      // Set the version
      versionArr[level] = (byte) version;
     
      // Set the version array
      setVersionArray(date, versionArr);
    }
   
  } // class ChannelFrame

}
TOP

Related Classes of tvbrowserdataservice.file.SummaryFile$ChannelFrame

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.