SimpleWapBrowserUI.java
上传用户:liming6160
上传日期:2022-06-07
资源大小:785k
文件大小:27k
源码类别:

J2ME

开发平台:

Java

  1. /*
  2.  *    MFRadio - stream radio client for Java 2 Micro Edition
  3.  *    Copyright (C) 2001 - 2007 Mobicom-Kavkaz, Inc
  4.  *    
  5.  *    Visit the project page at: http://mfradio.sourceforge.net
  6.  *
  7.  *    This program is free software; you can redistribute it and/or modify
  8.  *    it under the terms of the GNU General Public License as published by
  9.  *    the Free Software Foundation; either version 2 of the License, or
  10.  *    (at your option) any later version.
  11.  *
  12.  *    This program is distributed in the hope that it will be useful,
  13.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  *    GNU General Public License for more details.
  16.  *
  17.  *    You should have received a copy of the GNU General Public License
  18.  *    along with this program; if not, write to the Free Software
  19.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  *    Java (TM) and all Java (TM)-based marks are a trademark or 
  22.  *    registered trademark of Sun Microsystems, Inc, in the United States 
  23.  *    and other countries.
  24.  */
  25. package ru.mobicomk.mfradio.ui;
  26. import java.io.IOException;
  27. import java.io.InputStream;
  28. import java.io.InputStreamReader;
  29. import java.io.UnsupportedEncodingException;
  30. import java.util.Stack;
  31. import java.util.Vector;
  32. import javax.microedition.io.Connector;
  33. import javax.microedition.lcdui.Choice;
  34. import javax.microedition.lcdui.Command;
  35. import javax.microedition.lcdui.CommandListener;
  36. import javax.microedition.lcdui.Displayable;
  37. import javax.microedition.lcdui.Image;
  38. import javax.microedition.lcdui.List;
  39. import ru.mobicomk.mfradio.Constants;
  40. import ru.mobicomk.mfradio.controller.UIController;
  41. import ru.mobicomk.mfradio.iface.ContentHandler;
  42. import ru.mobicomk.mfradio.util.Locale;
  43. import ru.mobicomk.mfradio.util.Utils;
  44. /**
  45.  * Simple WAP/HTML link extractor and browser.
  46.  *
  47.  * <p>This browser gets WAP/HTML page from given location and make list of 
  48.  * links extracted from this page, then display it.</p>
  49.  *
  50.  * <p>This browser recognize links to <i>MP3</i> or <i>M3U</i> files and 
  51.  * links to other WAP/HTML pages. Other links recognized as unknown.</p>
  52.  *
  53.  * <p>User can selects other pages (by pressing to WAP/HTML pages links). Link 
  54.  * to <i>MP3</i> or <i>M3U</i> file will be added to application's playlist if
  55.  * user selects it.</p>
  56.  *
  57.  * @author  Roman Bondarenko
  58.  */
  59. public class SimpleWapBrowserUI extends List implements CommandListener, ContentHandler {
  60.     
  61.     private UIController uiController_;
  62.     private List menuList_;
  63.     private Vector names_   = new Vector();
  64.     private Vector urls_    = new Vector();
  65.     private Stack history_  = new Stack();
  66.     private Stack historyIndex_ = new Stack();
  67.     private String currURL_ = null;
  68.     
  69.     private String queryDefault_;
  70.     private String queryInputURL_;
  71.     // Commands
  72.     private Command exitCommand_;
  73.     private Command backCommand_;
  74.     private Command openCommand_;
  75.     private Command selectCommand_;
  76.     private Command saveCommand_;
  77.     private Command menuCommand_;
  78.     private Command refreshCommand_;
  79.     private Command helpCommand_;
  80.     
  81.     private Command allCommands_[] = {
  82.         exitCommand_
  83.             , backCommand_
  84.             , openCommand_
  85.             , selectCommand_
  86.             , saveCommand_
  87.             , menuCommand_
  88.             , refreshCommand_
  89.             , helpCommand_
  90.     };
  91.     
  92.     // UI components
  93.     private static final int ICON_LINK                  = 0;
  94.     private static final int ICON_NOT_LINK              = 1;
  95.     private static final int ICON_UNKNOWN               = 2;
  96.     private static final String[] iconPaths_ = { 
  97.           "/i/doc.png"          // 0
  98.         , "/i/doc3.png"    // 1
  99.         , "/i/unknown.png"      // 2
  100.     };
  101.     private Image[] icons_ = new Image[iconPaths_.length];
  102.     /**
  103.      * Create and initialize new browser object.
  104.      * @param title Browser title.
  105.      * @param uiController Application controller object.
  106.      */
  107.     public SimpleWapBrowserUI(String title, UIController uiController) {
  108.         super(title, Choice.IMPLICIT);
  109.         uiController_ = uiController;
  110.         
  111.         initUI();
  112.         initCommands();
  113.     }
  114.     /**
  115.      * Displays links list or page from given location.
  116.      * @param url URL of the WAP/HTML page.
  117.      * @param selectedIndex Index of selected link.
  118.      * @see ru.mobicomk.mfradio.ui.SimpleWapBrowserUI
  119.      */
  120.     public void displayHTML(final String url, final int selectedIndex) {
  121.         uiController_.beginWaitScreenRequested(uiController_.getLocale().getString(Constants.STR_Loading), true);
  122.         
  123.         new Thread(new Runnable() {
  124.             public void run() {
  125.                 displayHTML_(url, selectedIndex);
  126.             }
  127.         }).start();
  128.     }
  129.     
  130.     // interface ContentHandler ////////////////////////////////////////////////
  131.     
  132.     /**
  133.      * {@link ContentHandler} interface implementation. Free all alloicated 
  134.      * resources.
  135.      * @see ru.mobicomk.mfradio.iface.ContentHandler
  136.      * @see ru.mobicomk.mfradio.iface.ContentHandler#close
  137.      */
  138.     public void close() {
  139.         clearLists();
  140.         history_.setSize(0);
  141.         historyIndex_.setSize(0);
  142.     }
  143.     
  144.     /**
  145.      * {@link ContentHandler} interface implementation. 
  146.      * @param url The link to resource.
  147.      * @return <b>true</b> if given URL points to HTML or WAP page, 
  148.      * <b>false</b> othrwise.
  149.      * @see ru.mobicomk.mfradio.iface.ContentHandler
  150.      * @see ru.mobicomk.mfradio.iface.ContentHandler#canHandle
  151.      */
  152.     public boolean canHandle(String url) {
  153.         return isHTMLorWML(url);
  154.     }
  155.     
  156.     /**
  157.      * {@link ContentHandler} interface implementation. Get and display page 
  158.      * referenced by given URL.
  159.      * @param name Link title. (NOTE: Ignored in this case).
  160.      * @param url The link to resource.
  161.      * @see ru.mobicomk.mfradio.iface.ContentHandler
  162.      * @see ru.mobicomk.mfradio.iface.ContentHandler#handle
  163.      */
  164.     public void handle(String name, String url) {
  165.         displayHTML(url, 0);
  166.     }
  167.     
  168.     // CommandListener implementation //////////////////////////////////////////
  169.     
  170.     /**
  171.      * {@link CommandListener} interface implementation. Respond to commands.
  172.      * @see javax.microedition.lcdui.CommandListener
  173.      * @see javax.microedition.lcdui.CommandListener#commandAction
  174.      */
  175.     public void commandAction(Command c, Displayable s) {
  176.         try {
  177.             // respond to menu items
  178.             if ((s == menuList_) && (menuList_ != null) && menuList_.isShown()) {
  179.                 int selIndex = menuList_.getSelectedIndex();
  180.                 uiController_.replaceCurrent(this);
  181.                 if (c == backCommand_) {
  182.                     return;
  183.                 }
  184.                 if (c == List.SELECT_COMMAND || c == selectCommand_) {
  185.                     c = menuItem2Command(selIndex);
  186.                     // fall through - the commands will then be handled as if
  187.                     // they came from the main example.list
  188.                 }
  189.             }
  190.             
  191.             if ((c == List.SELECT_COMMAND && isShown()) || (c == openCommand_)) {
  192.                 gotoURL(getSelectedIndex());
  193.             } else if (c == saveCommand_) {
  194.                 saveToPlaylist(getSelectedIndex());
  195.             } else if (c == backCommand_) {
  196.                 goBack();
  197.             } else if (c == refreshCommand_) {
  198.                 refresh();
  199.             } else if (c == menuCommand_) {
  200.                 showMenu(getSelectedIndex());
  201.             } else if (c == exitCommand_) {
  202.                 exit();
  203.             } else if (c == helpCommand_) {
  204.                 help();
  205.             }
  206.         } catch (Throwable t) {
  207.             internalError(t, "in commandAction");
  208.         }
  209.     }
  210.     // Privates ////////////////////////////////////////////////////////////////
  211.     
  212.     private void displayHTML_(final String url, final int selectedIndex) {
  213. final Locale locale = uiController_.getLocale();
  214.         try {
  215.             
  216.             uiController_.progressMessage(locale.getString(Constants.STR_Clearing));
  217.             uiController_.updateProgress(); // exception
  218.             boolean error = false;
  219.             clearLists();
  220.             currURL_ = url;
  221.             
  222.             uiController_.progressMessage(locale.getString(Constants.STR_Connecting));
  223.             uiController_.updateProgress(); // exception
  224.             
  225.             try {
  226.                 readHTML(url);
  227.             } catch (Exception e) {
  228.                 append("["+Utils.friendlyException(e)+"]", null);
  229.                 //uiController_.log("displayHTML >> " + e.toString());
  230.                 error = true;
  231.             }
  232.             
  233.             uiController_.updateProgress(); // exception
  234.             
  235.             addCommand(backCommand_);
  236.             setCommandListener(this);
  237.             
  238.             String url0;
  239.             String name0;
  240.             int iconIdx;
  241.             
  242.             if (!error) {
  243.                 uiController_.progressMessage(locale.getString(Constants.STR_Rendering));
  244.                 for (int i=0; i<names_.size(); i++) {
  245.                     url0 = (String)urls_.elementAt(i);
  246.                     
  247.                     iconIdx = ICON_UNKNOWN;
  248.                     if (isHTMLorWML(url0)) {
  249.                         iconIdx = ICON_LINK;
  250.                     } else if (uiController_.canHandleLink(url0)) {
  251.                         iconIdx = ICON_NOT_LINK;
  252.                     }
  253.                     
  254.                     append((String) names_.elementAt(i), icons_[iconIdx]);
  255.                     uiController_.updateProgress(); // exception
  256.                 }
  257.                 setListIndex(selectedIndex);
  258.                 addCommand(menuCommand_);
  259.             } else {
  260.                 addCommand(refreshCommand_);
  261.             }
  262.             uiController_.updateProgress();
  263.         } catch (InterruptedException ex) {
  264.             int sel = append("["+ex.getMessage()+"]", null);
  265.             setListIndex(sel);
  266.             removeCommand(menuCommand_);
  267.             addCommand(refreshCommand_);
  268.             addCommand(backCommand_);
  269.             setCommandListener(this);
  270.         } finally {
  271.             uiController_.endWaitScreenRequested(this);
  272.         }
  273.     }
  274.     
  275.     private void initCommands() {
  276. final Locale locale  = uiController_.getLocale();
  277. exitCommand_    = new Command(locale.getString(Constants.STR_Exit_Browser), Command.EXIT, 1);
  278. backCommand_    = new Command(locale.getString(Constants.STR_Back), Command.BACK, 1);
  279. openCommand_    = new Command(locale.getString(Constants.STR_Open), Command.ITEM, 1);
  280. selectCommand_  = new Command(locale.getString(Constants.STR_Select), Command.ITEM, 1);
  281. saveCommand_    = new Command(locale.getString(Constants.STR_Save_in_playlist), Command.ITEM, 1);
  282. menuCommand_    = new Command(locale.getString(Constants.STR_Menu), Command.ITEM, 1);
  283.         refreshCommand_ = new Command(locale.getString(Constants.STR_Refresh), Command.ITEM, 1);
  284.     }
  285.     
  286.     private void initUI() {
  287.         for (int i = 0; i < iconPaths_.length; i++) {
  288.             try {
  289.                 icons_[i] = Image.createImage(iconPaths_[i]);
  290.             } catch (IOException ioe) { 
  291.             
  292.             }
  293.         }
  294.     }
  295.     
  296.     private void exit() {
  297.         currURL_ = null;
  298.         clearLists();
  299.         uiController_.exitWapBrowserRequested();
  300.     }
  301.     private void help() {
  302.         uiController_.showHelp(HelpUI.HELP_WAPBROWSERUI_TOPIC);
  303.     }
  304.     
  305.     private static void internalError(Throwable t, String desc) {
  306.         // TODO: add log entry
  307.     }
  308.     
  309.     private void clearLists() {
  310.         names_.setSize(0);
  311.         urls_.setSize(0);
  312.         
  313.         deleteAll();
  314.         
  315.         removeCommand(refreshCommand_);
  316.         removeCommand(menuCommand_);
  317.         
  318.         System.gc();
  319.     }
  320.     
  321.     private Command menuItem2Command(int index) {
  322.         // go through all commands and test if their label
  323.         // matches this menuList_ item's string
  324.         if ((index < 0) || (index >= menuList_.size())) {
  325.             return null;
  326.         }
  327.         String menuStr = menuList_.getString(index);
  328.         for (int i = 0; i < allCommands_.length; i++) {
  329.             if (allCommands_[i].getLabel().equals(menuStr)) {
  330.                 return allCommands_[i];
  331.             }
  332.         }
  333.         return null;
  334.     }
  335.     
  336.     private void showMenu(int index) {
  337.         String url=(String) urls_.elementAt(index);
  338.         boolean html = isHTMLorWML(url);
  339.         
  340.         menuList_ = new List(uiController_.getLocale().getString(Constants.STR_Menu), Choice.IMPLICIT);
  341.         menuList_.setCommandListener(this);
  342.         
  343.         if (html) {
  344.             menuList_.append(openCommand_.getLabel(), null);
  345.         } else {
  346.             menuList_.append(saveCommand_.getLabel(), null);
  347.         }
  348.         menuList_.append(refreshCommand_.getLabel(), null);
  349.         menuList_.append(helpCommand_.getLabel(), null);
  350.         menuList_.append(exitCommand_.getLabel(), null);
  351.         
  352.         menuList_.addCommand(backCommand_);
  353.         menuList_.addCommand(selectCommand_);
  354.         
  355.         uiController_.replaceCurrent(menuList_);
  356.     }
  357.     
  358.     private void gotoURL(int index) {
  359.         String url = (String) urls_.elementAt(index);
  360.         gotoURL(url, index);
  361.     }
  362.     
  363.     private void gotoURL(String url, int index) {
  364.         try {
  365.             if (index >= names_.size() || isHTMLorWML(url)) {
  366.                 if (currURL_ != null) {
  367.                     history_.push(currURL_);
  368.                     historyIndex_.push(new Integer(index));
  369.                 }
  370.                 displayHTML(url, 0);
  371.             } else {
  372.                 saveToPlaylist(index);
  373.             }
  374.         } catch (Exception e) {
  375.             uiController_.showError(Utils.friendlyException(e), this);
  376.         }
  377.         
  378.         /*
  379.         if (Utils.DEBUG) {
  380.             Utils.debugOut("SimpleHttpBrowser: after gotoURL. History contains "+history_.size()+" entries.");
  381.             for (int i = history_.size()-1; i>=0; i--) {
  382.                 Utils.debugOut("     "+i+": "+((String) history_.elementAt(i)));
  383.             }
  384.         }
  385.         */
  386.     }
  387.     
  388.     private void goBack() {
  389.         /*
  390.         if (Utils.DEBUG) {
  391.             Utils.debugOut("SimpleHttpBrowser: before goBack. History contains "+history_.size()+" entries.");
  392.             for (int i = history_.size()-1; i>=0; i--) {
  393.                 Utils.debugOut("     "+i+": "+((String) history_.elementAt(i))+"  #"+((Integer) historyIndex_.elementAt(i)));
  394.             }
  395.         }
  396.         */
  397.         
  398.         if (!history_.empty()) {
  399.             String url = (String) history_.pop();
  400.             int index = ((Integer) historyIndex_.pop()).intValue();
  401.             displayHTML(url, index);
  402.         } else {
  403.             exit();
  404.         }
  405.     }
  406.     
  407.     private void refresh() {
  408.         int selIndex = getSelectedIndex();
  409.         // Utils.debugOut("SimpleHttpBrowser.Refresh: index "+selIndex);
  410.         displayHTML(currURL_, selIndex);
  411.     }
  412.     
  413.     // somehow this doesn't work if there was a screen switch before !
  414.     private void setListIndex(int index) {
  415.         if (index>=0 && index<size()) {
  416.             setSelectedIndex(index, true);
  417.         }
  418.     }
  419.     
  420.     /*
  421.      * main parsing function
  422.      */
  423.     private void readHTML(String source) throws Exception {
  424. final Locale locale  = uiController_.getLocale();
  425.         uiController_.updateProgress();    
  426.         //uiController_.log("readHTML >> try to open connection");
  427.         
  428.         InputStream is0=Connector.openInputStream(source);
  429.         InputStreamReader in = null;
  430.         
  431.         uiController_.progressMessage(locale.getString(Constants.STR_Reading));        
  432.         uiController_.updateProgress();    
  433.     
  434.         try {
  435.             in = new InputStreamReader(is0, "UTF-8");            
  436.         } catch (UnsupportedEncodingException ex) {
  437.             //uiController_.log("readString >> UTF-8: " + ex.toString());
  438.             try {
  439.                 in = new InputStreamReader(is0, "UTF8");
  440.             } catch (UnsupportedEncodingException ex1) {
  441.                 //uiController_.log("readString >> UTF8: " + ex1.toString());
  442.                 in = new InputStreamReader(is0);
  443.             }
  444.         }
  445.         
  446.         //uiController_.log("readHTML >> connection opened!");
  447.         uiController_.updateProgress();
  448.         try {
  449.             String url;
  450.             String name;
  451.             String[] base=Utils.splitURL(source);
  452.             
  453.             //uiController_.log("readHTML >> source="+Utils.mergeURL(base));
  454.             
  455.             while ((url = readHref(in)) != null) {
  456.                 //uiController_.log("readHTML >> url="+url);
  457.                 
  458.                 uiController_.updateProgress();
  459.                 //uiController_.log("readHTML >> join with base");
  460.                 String[] splitU = joinURLs(base, url);
  461.                 
  462.                 //uiController_.log("readHTML >> merge");
  463.                 url = Utils.mergeURL(splitU);
  464.                 
  465.                 //uiController_.log("readHTML >> url="+url);
  466.                 
  467.                 // do not include those sort links in file listings.
  468.                 if (splitU[4].indexOf('?')!=0) {
  469.                     name = readHrefName(in);
  470.                     
  471.                     //uiController_.log("readHTML >> Read name=""+name+"" with url=""+url+""");
  472.                     
  473.                     names_.addElement(name);
  474.                     urls_.addElement(url);
  475.                 }
  476.             }
  477.             
  478.             if (names_.size()==0) {
  479.                 throw new Exception(locale.getString(Constants.STR_No_links_found + source));
  480.             }
  481.         } catch (Exception ex) {
  482.             //uiController_.log("readHTML >> " + ex.toString());
  483.             in.close();
  484.             in = null;
  485.             throw ex;
  486.         } finally {
  487.             //uiController_.log("readHTML >> close IS");
  488.             if (in != null) {
  489.                 in.close();
  490.                 in = null;
  491.             }
  492.         }
  493.         
  494.     }
  495.     
  496.     // URL methods /////////////////////////////////////////////////////////////
  497.     
  498.     private static boolean isHTMLorWML(String url) {
  499.         try {
  500.             String[] sURL = Utils.splitURL(url);
  501.             return sURL[0].equals("http")
  502.             && (sURL[4]=="" // no filename part
  503.                 || sURL[4].indexOf(".") == -1
  504.                 || sURL[4].indexOf(".wmlc") == sURL[4].length()-5
  505.                 || sURL[4].indexOf(".wml") == sURL[4].length()-4
  506.                 || sURL[4].indexOf(".html") == sURL[4].length()-5
  507.                 || sURL[4].indexOf(".htm") == sURL[4].length()-4);
  508.         } catch (Exception e) {
  509.             internalError(e, "isHTMLorWML()");
  510.             return false;
  511.         }
  512.     }
  513.     
  514.     private String[] joinURLs(String[] url, String relPath) throws Exception {
  515.         String[] rel=Utils.splitURL(relPath);
  516.         String[] result=new String[6];
  517.         
  518.         result[0]=(rel[0]=="")?url[0]:rel[0];
  519.         result[1]=(rel[1]=="")?url[1]:rel[1];
  520.         result[2]=(rel[2]=="")?url[2]:rel[2];
  521.         
  522.         if (rel[3].length()>0) {
  523.             if (rel[3].charAt(0)=='/') {
  524.                 // absolute path given
  525.                 result[3]=rel[3];
  526.             } else {
  527.                 result[3]=url[3]+'/'+rel[3];
  528.             }
  529.         } else {
  530.             result[3]=url[3];
  531.         }
  532.         
  533.         result[4]=(rel[4]=="")?url[4]:rel[4];
  534.         result[5]=(rel[5]=="")?url[5]:rel[5];
  535.         
  536.         return result;
  537.     }
  538.     
  539.     
  540.     // beware: highly optimized HTML parsing code ahead !
  541.     
  542.     private boolean charEquals(char char1, char char2, boolean caseSensitive) {
  543.         boolean equal=(char1==char2);
  544.         if (!equal && !caseSensitive
  545.             && ((char1>=0x41 && char1<=0x5A) || (char1>=0x41 && char1<=0x5A))) {
  546.             equal=((char1^0x20)==char2);
  547.         }
  548.         return equal;
  549.     }
  550.     
  551.     
  552.     private boolean skip(InputStreamReader is, String until, boolean onlyWhiteSpace, boolean caseSensitive) throws Exception {
  553. final Locale locale  = uiController_.getLocale();
  554.         //if (TRACE) System.out.println("skip(is, ""+until+"", onlyWhiteSpace="+onlyWhiteSpace+", caseSensitive="+caseSensitive+")");
  555.         int len=until.length();
  556.         int found=0;
  557.         int v=is.read();
  558.         while (v>0) {
  559.             if (v==0) {
  560.                 // binary data
  561.                 throw new Exception(locale.getString(Constants.STR_Incorrect_data_format));
  562.             }
  563.             boolean equal=charEquals((char) v, until.charAt(found), caseSensitive);
  564.             //uiController_.log("Read '"+((char) v)+ "' ("+v+")" + " found="+found+"  equal="+equal);
  565.             if (!equal) {
  566.                 if (onlyWhiteSpace && v>32) {
  567.                     throw new Exception(locale.getString(Constants.STR_Incorrect_data_format));
  568.                 }
  569.                 if (found>0) {
  570.                     found=0;
  571.                     continue;
  572.                 }
  573.             } else {
  574.                 found++;
  575.             }
  576.             if (found==len) {
  577.                 return true;
  578.             }
  579.             v=is.read();
  580.         }
  581.         return false;
  582.     }
  583.     
  584.     // if a character other than white space is found, it is returned
  585.     private int findDelimiter(InputStreamReader is) throws Exception {
  586.         //if (TRACE) System.out.println("findDelimiter(is)");
  587.         while (true) {
  588.             int v=is.read();
  589.             if (v==-1) {
  590.                 return -1;
  591.             }
  592.             if (v==0) {
  593.                 // binary data
  594.                 throw new Exception(uiController_.getLocale().getString(Constants.STR_Incorrect_data_format));
  595.             }
  596.             if (v>32) {
  597.                 return v;
  598.             }
  599.         }
  600.     }
  601.     
  602.     private String readString(InputStreamReader is
  603.         , char firstChar
  604.         , String delim
  605.         , boolean delimCaseSensitive) throws Exception {
  606.         
  607.         //uiController_.log("readString >> firstChar='"+firstChar+"', delim='"+delim+"', delimCaseSensitive="+delimCaseSensitive);
  608.         
  609.         StringBuffer sb = new StringBuffer();
  610.         boolean lastWhiteSpace=false;
  611.         
  612.         if (firstChar!=0) {
  613.             sb.append(firstChar);
  614.             lastWhiteSpace=(firstChar<=32);
  615.         }
  616.         
  617.         int v;
  618.         boolean inTag=false;
  619.         int found=0;
  620.         int len=delim.length();
  621.         int appendedInDelim=0;
  622.         
  623.         while (true) {
  624.             v=is.read();
  625.             if (v==-1) {
  626.                 throw new Exception(uiController_.getLocale().getString(Constants.STR_Unterminated_string));
  627.             }
  628.             if (v<=32) {
  629.                 // whitespace
  630.                 if (lastWhiteSpace) {
  631.                     continue;
  632.                 }
  633.                 v=32;
  634.                 lastWhiteSpace=true;
  635.             } else {
  636.                 lastWhiteSpace=false;
  637.                 if (v=='<') {
  638.                     inTag=true;
  639.                 }
  640.             }
  641.             boolean equal=charEquals((char) v, delim.charAt(found), delimCaseSensitive);
  642.             //if (TRACE) System.out.println("ReadString '"+((char) v)+"' found="+found+"  equal="+equal);
  643.             if (!equal) {
  644.                 if (found>0) {
  645.                     found=0;
  646.                     appendedInDelim=0;
  647.                     equal=charEquals((char) v, delim.charAt(found), delimCaseSensitive);
  648.                 }
  649.             }
  650.             if (equal) {
  651.                 found++;
  652.                 if (found==len) {
  653.                     if (appendedInDelim>0) {
  654.                         sb.setLength(sb.length()-appendedInDelim);
  655.                     }
  656.                     break;
  657.                 }
  658.             }
  659.             if (!inTag) {
  660.                 sb.append((char) v);
  661.                 // when we are inside the delimiter, we want to get rid of the delimiter later
  662.                 // so track it
  663.                 if (found>0) {
  664.                     appendedInDelim++;
  665.                 }
  666.             } else if (v=='>') {
  667.                 inTag=false;
  668.             }
  669.         }
  670.         //uiController_.log("readString >> ret: '"+sb.toString()+"'");
  671.         return sb.toString();
  672.     }
  673.     
  674.     
  675.     /*
  676.      * Simplified parser to find xyz of a <a href="xyz">blablabla</a> statement
  677.      */
  678.     private String readHref(InputStreamReader is) throws Exception {
  679.         //uiController_.log("readHref >> enter");
  680.         uiController_.updateProgress();
  681.         
  682.         // first skip everything until "<a"
  683.         if (!skip(is, "<a", false, false)) {
  684.             //uiController_.log("readHref >> [first skip everything until '<a'] ret: null! (1)");
  685.             return null;
  686.         }
  687.         uiController_.updateProgress();
  688.         // read "href"
  689.         if (!skip(is, "href", false, false)) {
  690.             //uiController_.log("readHref >> [read 'href'] ret: null! (2)");
  691.             return null;
  692.         }
  693.         uiController_.updateProgress();
  694.         // read until "="
  695.         if (!skip(is, "=", true, true)) {
  696.             //uiController_.log("readHref >> [read until '='] ret: null! (3)");
  697.             return null;
  698.         }
  699.         uiController_.updateProgress();
  700.         // wait for " or ' or nothing
  701.         int delim=findDelimiter(is);
  702.         char endDelim=(char) delim;
  703.         char firstChar=0;
  704.         if (delim!='"' && delim!=''') {
  705.             // url not enclosed in quotes
  706.             endDelim='>';
  707.             firstChar=(char) delim;
  708.         }
  709.         uiController_.updateProgress();
  710.         String ret = readString(is, firstChar, ""+endDelim, true);
  711.         
  712.         //uiController_.log("readHref >> URL="+ret);
  713.         
  714.         uiController_.updateProgress();
  715.         if (firstChar==0) {
  716.             if (!skip(is, ">", true, true)) {
  717.                 //uiController_.log("readHref >> [read until '>'] ret: null! (4)");
  718.                 return null;
  719.             }
  720.         }
  721.         uiController_.updateProgress();
  722.         //uiController_.log("readHref >> ret: " + ret);
  723.         return ret;
  724.     }
  725.     
  726.     /**
  727.      * Simplified parser to find blabla of a <a href="xyz">blablabla</a> statement
  728.      */
  729.     private String readHrefName(InputStreamReader is) throws Exception {
  730.         // the stream is at first char after >. We just read the string until we find "</a>"
  731.         String ret = readString(is, (char) 0, "</a>", false);
  732.         return ret;
  733.     }
  734.     private void saveToPlaylist(int idx) {
  735.         uiController_.handleLink((String) names_.elementAt(idx)
  736.             , (String) urls_.elementAt(idx));
  737.     }
  738. }