sql cheat sheet:
UPDATE channel LEFT JOIN station USING (stationid) SET channel.disabled = true WHERE channel.disabled is true AND station.disabled is not true;
INSERT device_channel (deviceid, channelid) SELECT 3, channelid FROM channel WHERE channelid > 20;


  static public void test()
  {
    String[][] test = {{"A","B","C", "1"}, {"D", "E", "F", "2"}, {"G", "H", "I", "3"}};
    List<String[]> a = Arrays.asList(test);
    List<List<String>> b = Util.newVector();
    for (String[] strs : a)
      b.add(Arrays.asList(strs));
    Util.dumpDColl("in", b);
    Vector<Vector<String>> c = Util.multiplySets(b);
    Util.dumpDColl("out", c);
    System.exit(1);
  }

  static public void dumpDColl(String info, Collection<? extends Collection<?>> cc)
  {
    String out = info + ": ";
    for (Collection<?> c : cc)
    {
      out += "(";
      for (Object e : c)
        out += e.toString();
      out += ") ";
    }
    System.out.println(out);
  }
  static public void dumpColl(String info, Collection<?> c)
  {
    String out = info + ": ";
    for (Object e : c)
      out += e.toString();
    System.out.println(out);
  }

  /**
   * Pick one from each set.
   * For (AB), (CD) returns (AC), (AD), (BC), (BD) in this order.
   * For (ABC), (DEF), (GHI) returns (ADG), (ADH), (ADI), (AEG), ...
   * @return All possible combinations
   */
  static public <E> Vector<Vector<E>> multiplySets(List<List<E>> sets)
  {
    Vector<Vector<E>> result = Util.newVector();
    if (sets.size() == 0)
      return Util.newVector();
    if (sets.size() == 1)
    {
      Vector<E> eResult = Util.newVector();
      for (E e : sets.get(0))
        eResult.add(e);
      result.add(eResult);
      return result;
    }
    System.out.println("size: " + sets.size());
    dumpDColl("multiply this", sets.subList(1, sets.size()));
    List<Vector<E>> others = multiplySets(sets.subList(1, sets.size()));
    dumpDColl("others", others);
    dumpColl("first set", sets.get(0));
    for (E e : sets.get(0))
    {
      System.out.println("e: " + e);
      for (List<E> other : others)
      {
        dumpColl("other", other);
        Vector<E> eResult = Util.newVector();
        eResult.add(e);
        eResult.addAll(other);
        dumpColl("eResult", eResult);
        result.add(eResult);
      }
    }
    dumpDColl("result", result);
    return result;
  }

  
  
  
  
  
    try {
      PlannedRecording first = overlap.firstElement();
      for (TVChannel channel : channelsForStation(first.stationID()))
      {
        for (Device device : devicesForChannel(channel))
        {
          for (PlannedRecording recording : overlap)
          {
            if (recording == first)
              continue;
            boolean canAll = true;
            Vector<? extends TVChannel> channels = channelsForStation(first.stationID());
            if (!device.canRecordSimultanously();
          }          
        }
      }
    } catch (Exception e) {}
  
  
  protected boolean canRecordSimultanouslyOld(Vector<PlannedRecording> overlap)
  {
    // maybe one device can reconrd them all
    Collection<TVChannel> allChannels = Util.newCollection();
    for (PlannedRecording r : overlap)
      allChannels.add(r.channel());
    if (overlap.firstElement().device().canRecordSimultanously(allChannels))
      return true;

    //for (Vector<PlannedRecording> overlap : overlaps)
    //{
      // maybe one device can reconrd them all
      //Collection<TVChannel> allChannels = Util.newCollection();
      for (PlannedRecording r : overlap)
        allChannels.add(r.channel());
      if (overlap.firstElement().device().canRecordSimultanously(allChannels))
        return true;//continue;

      // test each item with all others, but every combination only once
      for (int i1 = 0; i1 < overlap.size(); i1++)
      {
        PlannedRecording test1 = overlap.elementAt(i1);
        // try all together first
        Collection<TVChannel> other = Util.newCollection();
        for (int i2 = i1 + 1; i2 < overlap.size(); i2++)
          other.add(overlap.elementAt(i2).channel());
        if (test1.device().canRecordSimultanously(other));
      }
    //}
      return false;
  }
  
  
      BufferedReader reader = new BufferedReader(new 
              InputStreamReader(myProcess.getInputStream()));
      String line;
      while ((line = reader.readLine()) != null)
      {
        System.out.println(line);
        if (line.contains("LOCK"))
          System.out.println("Have lock");          
      }
  
  
  
  
  /*
  protected int writeTransponderToDB(Transponder dsTransponder)
  {
    //rs = DB.me.database().createStatement().executeQuery("SELECT * FROM channel INNER JOIN dvb_transponder USING (dvb_transponderid) LEFT JOIN station ON (channel.stationid = station.stationid) WHERE channelid = " + myChannelID);
    PreparedStatement addChannel = DB.me.database().prepareStatement("INSERT INTO channel (longname, shortname, rec_group, keep) SELECT filmid, rec_group, keep FROM recording WHERE recordid = ?", Statement.RETURN_GENERATED_KEYS);
  }

  protected int writeChannelToDB(int dTransponder, DVBChannel dsChannel)
    throws SQLException
  {
    PreparedStatement findTransponder = DB.me.database().prepareStatement("SELECT * FROM dvb_transponder WHERE dvb_multiplexid = ?");
    PreparedStatement addChannel = DB.me.database().prepareStatement("INSERT INTO channel (longname, shortname, rec_group, keep) SELECT filmid, rec_group, keep FROM recording WHERE recordid = ?", Statement.RETURN_GENERATED_KEYS);
  }
  */
  
  


  
  public void scanAllChannels()
    throws Misconfiguration, SQLException, CantUseTVCard
  {
    Util.debug("scan all channels");
    for (TVChannel channel : CoreData.me.myChannels.values())
    {
      if (myCard.canRecord(channel))
        scanTransponder((DVBChannel) channel);
    }
  }

  
  /**
   * Reads only one NIT, on the startChannel
   * Doesn't work in practice, because no NIT contains all channels.
   */
  public Vector<Transponder> scanForTransponders(DVBChannel startChannel)
    throws Misconfiguration, CantUseTVCard
  {
    Vector<Transponder> result = Util.newVector();
    Transponder startTransponder = new Transponder();
    startTransponder.tuning = startChannel;

    // Read NIT, to learn all the frequencies
    tuneToTransponder(startTransponder.tuning);
    try {
      result.addAll(scanNIT(startTransponder));
    } finally {
      done();
    }

    // Tune to each transponder, and scan each for the channels on it
    for (Transponder dsTransponder : result)
    {
      try {
        tuneToTransponder(dsTransponder.tuning);
        try {
          scanTunedTransponder(dsTransponder.tuning);
        } finally {
          done();
        }
      } catch (Exception e) {
        Util.error("Scan of transponder with freq " + dsTransponder.tuning.frequency + " failed", e);
      }
    }
    return result;
  }

  
    final boolean kThreaded = false;
    //final Object signal = new Object();
    for (final DVBChannel dsChannel : dsTransponder.channels)
    {
      if ( ! kThreaded)
        scanForPIDs(dsChannel.serviceID, dsChannel);
      else // threaded
      {
        final String name = "Scan for PIDs of channel " + dsChannel.serviceID;
        Thread t = new Thread(name)
        {
          @Override
          public void run()
          {
            Util.debug(name);
            try {
              scanForPIDs(dsChannel.serviceID, dsChannel);
            } catch (Exception e) { Util.error(e); }
            //signal.notifyAll(); // TODO throws IllegalMonitorStateException
          }
        };
        t.start();
      }
    }
    if (kThreaded)
    {
      boolean wait = false;
      while (wait)
      {
        wait = false;
        for (final DVBChannel dsChannel : dsTransponder.channels)
          if (dsChannel.pmtPID < 0)
            wait = true;
        if (wait)
          try {
            //signal.wait(10000);
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            throw new CantUseTVCard("Scan for PIDs took too long (more than 10s)");
            // TODO what to do with file handles of scanForPIDs()?
          }
      }
    }
