////////////////////////////////////////////////////////////////////
//:LinkChecker
//
// Author: Igor Maslov
//
// This program allows to open a Web page and to check the
// validity of all links. Program is implemented as Java
// application and uses JDK1.1.5 and Swing1.0.2.
//
// To start the program, user types:
//       java LinkChecker
////////////////////////////////////////////////////////////////////

import com.sun.java.swing.*;
import com.sun.java.swing.tree.*;
import com.sun.java.swing.text.*;
import com.sun.java.swing.JComponent;

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import java.util.*;
import sun.audio.*;

public class LinkChecker extends JFrame {

  public static Dimension dim=new Dimension(800,480);
  static final int port=80;
  static final String tagTok="<>";
  static int threadCase;
  String webPage="New page";
  String mailHost=null;
  String mailFrom=null;
  String selectLink=null;
  LinkTok linkTok[]={new LinkTok("HREF=",6),new LinkTok("href=",6),
    new LinkTok("SRC=",5),new LinkTok("src=",5),
    new LinkTok("ACTION=",8),new LinkTok("action=",8)};

  Vector badLink=new Vector();
  Vector mailTo=new Vector();
  LinkTree linkTree;
  DefaultTreeModel treeModel;
  LinkObj rootLink=new LinkObj(webPage,true,true,0);
  DefaultMutableTreeNode rootNode=
    new DefaultMutableTreeNode(rootLink);
  
  JFrame parentFrame;
  JLabel statLabel=new JLabel("Status Bar",JLabel.LEFT);
  JTextField statField=new JTextField("",40);
  TextArea txtArea=new TextArea("",24,80,TextArea.SCROLLBARS_BOTH);
  JMenuBar menuBar=new JMenuBar();
  JMenu filMenu=new JMenu("File");
  JMenu lnkMenu=new JMenu("Link");
  JMenu mailMenu=new JMenu("Mail");
  JMenuItem[] fil={
    new JMenuItem("Open Page"),
    new JMenuItem("Reopen in Browser"),
    new JMenuItem("Exit")
  };
  JMenuItem[] lnk={
    new JMenuItem("Show All"),
    new JMenuItem("Check All"),
    new JMenuItem("Check Link"),
    new JMenuItem("Open Link"),
    new JMenuItem("Reopen in Browser")    
  };
  JMenuItem[] mail={
    new JMenuItem("Get Address"),
    new JMenuItem("Send Mail")
  };
  
// This is constructor for class LinkChecker
  
  public LinkChecker(){
    parentFrame=this;
    setTitle("WebPage Links Checker");
    for(int i=0;i<fil.length;i++)
      filMenu.add(fil[i]);
    for(int i=0;i<lnk.length;i++)
      lnkMenu.add(lnk[i]);
    for(int i=0;i<mail.length;i++)
      mailMenu.add(mail[i]);
    fil[0].addActionListener(new OpenL());
    fil[1].addActionListener(new BrowseL());
    fil[2].addActionListener(new ExitL());
    lnk[0].addActionListener(new ShowL());
    lnk[1].addActionListener(new AllL());
    lnk[2].addActionListener(new CheckL());
    lnk[3].addActionListener(new CheckL());
    lnk[4].addActionListener(new BrowseLL());
    mail[0].addActionListener(new AddrL());
    mail[1].addActionListener(new SendL());        
    menuBar.add(filMenu);
    menuBar.add(lnkMenu);
    menuBar.add(mailMenu);
    setJMenuBar(menuBar);
    
    getContentPane().setLayout(new GridLayout(1,2));
    
    linkTree=new LinkTree(rootNode);
    treeModel=(DefaultTreeModel)linkTree.getModel();
    //linkTree.setCellRenderer(new LinkTreeCellRenderer());
    
    JScrollPane treePane=new JScrollPane(linkTree);
    getContentPane().add(treePane);    
    
    txtArea.setEditable(false);
    getContentPane().add(txtArea);

  }
  
// This method uses "mailto" protocol to send e-mail 
// to the owner of the Web page in case there are 
// invalid links

  boolean sendMail(String[] badL){
    String[] msg={"Message sent to the recipient",
      "Error sending message"};
    if(mailHost==null){
      mailHost=JOptionPane.showInputDialog
        (null,"Enter mailhost for your computer","Input",
        JOptionPane.QUESTION_MESSAGE);
      repaint();
      treeModel.reload(rootNode);                
      if(mailHost==null) return false;
      System.getProperties().put("mail.host",mailHost);              
    }
    if(mailFrom==null){
      mailFrom=JOptionPane.showInputDialog
        (null,"Enter your e-mail address","Input",
        JOptionPane.QUESTION_MESSAGE);
      repaint();
      treeModel.reload(rootNode);                
      if(mailFrom==null) return false;
    }
    String to=JOptionPane.showInputDialog
      (null,"Enter recipient's e-mail address",
      "Input",JOptionPane.QUESTION_MESSAGE);
    repaint();
    treeModel.reload(rootNode);              
    if(to==null) return false; 
      
    try{
      URL m=new URL("mailto:"+to);
      URLConnection cnctM=m.openConnection();
      cnctM.setDoOutput(true);
      cnctM.setDoInput(false);
      cnctM.connect();
    
      PrintWriter outStream=new PrintWriter
        (new OutputStreamWriter(cnctM.getOutputStream()));

      outStream.println("From: "+mailFrom);
      outStream.println("To: "+to);
      outStream.println("Subject: Invalid links");
      outStream.println("");
      outStream.println("On the following WebPage:");
      outStream.println(webPage);
      outStream.println("you have invalid links:");
      for(int i=0;i<badL.length;i++)
        outStream.println(badL[i]);
      outStream.close();
      JOptionPane.showMessageDialog
        (null,msg[0],"",JOptionPane.INFORMATION_MESSAGE);
      repaint();
      treeModel.reload(rootNode);              
      return true;
    }
    catch(Exception e1){
      JOptionPane.showMessageDialog
        (null,msg[1],"Error",JOptionPane.ERROR_MESSAGE);
      repaint();
      treeModel.reload(rootNode);              
      return false;
    }
  }      
    
// This method returns selected link in the tree

  DefaultMutableTreeNode getSelectedNode(){
    TreePath selPath=linkTree.getSelectionPath();
    if(selPath !=null)
      return (DefaultMutableTreeNode)selPath.getLastPathComponent();
    else{
      return null;
    }
  }

// This class is used for parsing the contents of the page

  static class LinkTok{
    String stringTok;
    int delta;
    LinkTok(String st,int d){
      stringTok=st;
      delta=d;
    }
  }
  
// This class implements link as an object

  class LinkObj{
    String linkAddr;
    boolean isValid;
    boolean isChecked;
    int servHits;
    LinkObj(String a,boolean v,boolean c,int n){
      linkAddr=a;
      isValid=v;
      isChecked=c;
      servHits=n;
    }
    void changeAddress(String s){
      linkAddr=s;
    }
    
    public String toString(){
      return linkAddr;
    }
  }                          
  
// This class is used for rendering the link tree

  class LinkTree extends JTree{
    LinkTree(DefaultMutableTreeNode tn){
      super(tn);
    }
  
    String convertValueToText
      (LinkObj link,boolean selected,boolean expanded,
      boolean leaf,int row,boolean hasFocus){
      return link.toString();
    }
  }

// This class is used for listing invalid links

  public class BadDialog extends Dialog{
    protected Button butOK;
    protected TextArea badArea;
    
    public BadDialog(Frame dad,String msg){
      super(dad,"Invalid links",false);
      this.setLayout(new BorderLayout(15,5));      
      badArea=new TextArea(msg,5,40,TextArea.SCROLLBARS_BOTH);
      butOK=new Button("OK");
      
      ActionListener listener=new ActionListener(){
        public void actionPerformed(ActionEvent e){
          BadDialog.this.dispose();
        }
      };
      
      butOK.addActionListener(listener);
      this.add("Center",badArea);
      Panel p=new Panel();
      p.setLayout(new FlowLayout(FlowLayout.CENTER,15,10));
      p.add(butOK);
      this.add("South",p);
      this.setLocation(dim.width/4,dim.height/4);      
      this.pack();
    }
  }

/////////////////////////////////////////////////////////////////////

  public class MailDialog extends Dialog{
    protected Button butOK;
    protected TextArea mailArea;
    
    public MailDialog(Frame dad,String msg){
      super(dad,"Mail addresses",false);
      this.setLayout(new BorderLayout(15,5));      
      mailArea=new TextArea(msg,3,40,TextArea.SCROLLBARS_BOTH);
      butOK=new Button("OK");
      
      ActionListener listener=new ActionListener(){
        public void actionPerformed(ActionEvent e){
          MailDialog.this.dispose();
        }
      };
      
      butOK.addActionListener(listener);
      this.add("Center",mailArea);
      Panel p=new Panel();
      p.setLayout(new FlowLayout(FlowLayout.CENTER,15,10));
      p.add(butOK);
      this.add("South",p);
      this.setLocation(dim.width/2,dim.height/2);      
      this.pack();
    }
  }

/////////////////////////////////////////////////////////////////////
class HtmlPanel extends JFrame implements Runnable{
  protected JEditorPane html;
  protected URL url;
  
  public HtmlPanel(String page){
    getContentPane().setLayout(new BorderLayout());
    try{
      url=new URL(page);
      html=new JEditorPane(url);
      html.setEditable(false);
      
      JScrollPane scroller=new JScrollPane();
      JViewport vp=scroller.getViewport();
      vp.add(html);
      vp.setBackingStoreEnabled(true);
      getContentPane().add(scroller,BorderLayout.CENTER);
    }
    catch(Exception e){
      System.out.println(e);
    }
  }
  
  public void run(){
    Document doc=html.getDocument();
    try{
      html.setPage(url);
    }
    catch(Exception e){
      System.out.println(e);
    }
  }
}
//////////////////////////////////////////////////////////////////////

/*
  public class LinkTreeCellRenderer extends JLabel
    implements TreeCellRenderer{
    
    protected Font defaultFont=new Font("SansSerif",0,12);
    protected ImageIcon collapsedIcon
      =new ImageIcon("collapsed.gif");
    protected ImageIcon expandedIcon
      =new ImageIcon("expanded.gif");

    public Component getTreeCellRendererComponent
      (JTree tree,Object value,boolean selected,
      boolean expanded,boolean leaf,int row,boolean hasFocus){
      
      Font font;
      LinkObj link=(LinkObj)value;
      //String stringValue=tree.convertValueToText
        //(value,selected,expanded,leaf,row,hasFocus);
      String stringValue=link.toString();
      setText(stringValue);
      
      if(expanded) setIcon(expandedIcon);
      else if(!leaf) setIcon(collapsedIcon);
      else setIcon(null);
      
      if(link.isValid) setForeground(Color.yellow);
      if(!(link.isValid)) setForeground(Color.red);
      
      setFont(defaultFont);
      
      //this.selected=selected;
      return this;
    }
  }
*/
  
// This thread is used for opening the source code of the page

  class OpenThread extends Thread {
    protected String page;
    
    OpenThread(String wp){
      page=wp;
    }
    
    public void run(){
      int insertPos;
      String line;
      URL pageURL;
      URLConnection cnctURL;
      BufferedReader inStream;
            
      try{
        pageURL=new URL(page);
      }
      catch(Exception e){
        threadCase=1;
        return;
      }
      
      try {
        cnctURL=pageURL.openConnection();
        cnctURL.setUseCaches(false);
      }
      catch(Exception e){
        threadCase=2;
        return;
      }
      
      try{
        inStream=new BufferedReader
          (new InputStreamReader(cnctURL.getInputStream()));
      }
      catch(Exception e){
        threadCase=4;
        return;
      }

      String type=cnctURL.getContentType();
      if((type.compareTo("text/html"))!=0){
        threadCase=3;
        return;
      }      
 
      txtArea.replaceRange("",0,60000);
      insertPos=0;      
      while(true){
        try{
          line=inStream.readLine();
          if(line==null)break;
          line+="\n";
          txtArea.insert(line,insertPos);
          insertPos+=line.length();
        }
        catch(IOException e){
          threadCase=5;
          return;
        }
      }
    }
  }
  
// This class is used to check the validity of the link

  class CheckLink implements Runnable{
    LinkObj link;
    CheckLink(LinkObj lnk){
      link=lnk;
    }
    
    public void run(){
      String line;
      String msg;
      URL lnkURL;
      URLConnection cnctLnk;
      BufferedReader inStream;
      String lnkName=link.toString();
      
      if(!(lnkName.startsWith("http"))) return;

      try{
        lnkURL=new URL(lnkName);
      }
      catch(Exception e1){
        link.isValid=false;
        return;
      }

      synchronized(lnkURL){      
      try{
        cnctLnk=lnkURL.openConnection();
        cnctLnk.setUseCaches(false);
      }
      catch(Exception e1){
        link.isValid=false;
        return;
      }
      
      try{
        inStream=new BufferedReader
          (new InputStreamReader(cnctLnk.getInputStream()));
      }
      catch(Exception e1){
        link.isValid=false;
        return;
      }

      String type=cnctLnk.getContentType();
      if((type.compareTo("text/html"))!=0) return;      
      
      
      for(int il=0;il<20;il++){
        try{
          String lin=inStream.readLine();
          if(lin==null) break;
          line=lin.toLowerCase();
          if((line.indexOf("file not found"))>=0){
            link.isValid=false; return;}
          if((line.indexOf("access forbidden"))>=0){
            link.isValid=false; return;}
          if((line.indexOf("access denied"))>=0){
            link.isValid=false; return;}
        }
        catch(Exception e1){
          link.isValid=false;
          return;
        }
      }
      }  //synchronized
    }
  }

// This class spawns OpenThread to open the Web page

  class OpenL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      String newPage;
      String msg;      
      String s=JOptionPane.showInputDialog
        (null,"Enter URL to open page","Input",
        JOptionPane.QUESTION_MESSAGE);
      repaint();
      treeModel.reload(rootNode);                
      if(s.regionMatches(true,0,"http",0,4)) newPage=s;
      else newPage="http://"+s;

      threadCase=0;
      OpenThread ot=new OpenThread(newPage);
      ot.start();
      try{
        ot.join();
      }
      catch(InterruptedException e1){
        threadCase=5;
      }  

      switch(threadCase){
        default:
        case 0: webPage=newPage;
                if(!(badLink.isEmpty()))
                  badLink.removeAllElements();
                rootLink.changeAddress(webPage);      
                rootNode.removeAllChildren();
                treeModel.reload(rootNode);
                repaint();
                return;
        case 1: msg="Invalid URL"; break;
        case 2: msg="Can not open this URL"; break;
        case 3: msg="This is not HTML document"; break;
        case 4: msg="Can not read from this URL"; break;
        case 5: msg="Error transferring data"; break;
      }
      JOptionPane.showMessageDialog
        (null,msg,"Error",JOptionPane.ERROR_MESSAGE);
      repaint();
      treeModel.reload(rootNode);                 
    }
  }
  
  class BrowseL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      HtmlPanel hp=new HtmlPanel(webPage);
      hp.setSize(800,400);
      hp.setVisible(true);    
      Thread t=new Thread(hp);
      t.start();
    }
  }

// This is a simple class to shut down

  class ExitL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      dispatchEvent(new WindowEvent(LinkChecker.this,
        WindowEvent.WINDOW_CLOSING));
    }
  }
    
// This class is invoked to parse the page and list all links

  class ShowL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      String text=new String(txtArea.getText());
      StringTokenizer tagST=new StringTokenizer(text,tagTok);
      int first,last;
      int index=0;
      LinkObj link=rootLink;
      DefaultMutableTreeNode node=rootNode;

      while(tagST.hasMoreTokens()){
        String st=tagST.nextToken();
        String sa;
        for(int i=0;i<6;i++){
          if((first=st.indexOf(linkTok[i].stringTok))<0)continue;
          else {
            last=st.indexOf("\"",first+linkTok[i].delta);
            if((last>first)&&(last>0)) 
              sa=st.substring(first+linkTok[i].delta,last);
            else{
              sa=st.substring(first+linkTok[i].delta);
              sa+="<"+tagST.nextToken()+">";
            }
            char bslash='\\';
            char slash='/'; 
            if((sa.indexOf(bslash))>=0)
              sa=sa.replace(bslash,slash);
            if((sa.indexOf(":"))<0){
              boolean sta=sa.startsWith("/");
              boolean en=webPage.endsWith("/");
              if(sta && en) sa.substring(1);
              if((!sta)&&(!en)) sa="/"+sa;
              sa=webPage+sa;
            }
            link=new LinkObj(sa,true,false,0);
            node=new DefaultMutableTreeNode(link);
            treeModel.insertNodeInto(node,rootNode,index);
            index++;
            break;
          }
        }
      }

      treeModel.reload(rootNode);
      repaint();
    }
  }
  
// This class is invoked when user requests to check all links

  class AllL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      String[] msg={"All links are valid",
        "Invalid links were found",
        "Send error message to the Owner?",
        "Sorry, the Owner has already been notified"};

      int kidCount=rootNode.getChildCount();
      LinkObj[] lob=new LinkObj[kidCount];
      DefaultMutableTreeNode[] nod=new DefaultMutableTreeNode[kidCount];
      CheckLink[] cl=new CheckLink[kidCount];
      Thread[] ct=new Thread[kidCount];      

      for(int i=0;i<kidCount;i++){
        nod[i]=(DefaultMutableTreeNode)rootNode.getChildAt(i);
        lob[i]=(LinkObj)nod[i].getUserObject();
        cl[i]=new CheckLink(lob[i]);
        ct[i]=new Thread(cl[i]);
        ct[i].start();
      }
      for(int i=0;i<kidCount;i++){
        try{
          ct[i].join();
        }
        catch(InterruptedException e1){
          lob[i].isValid=false;
        }
      }

      for(int i=0;i<kidCount;i++){
        lob[i].isChecked=true;
      }
      int numBad=0;
      for(int i=0;i<kidCount;i++){
        if(!(lob[i].isValid))
          numBad++;
      }
      
      if(numBad==0){
        JOptionPane.showMessageDialog
          (null,msg[0],"",JOptionPane.INFORMATION_MESSAGE);
        repaint();
        treeModel.reload(rootNode);                
        return;
      }                       
      
      else {
        JOptionPane.showMessageDialog
          (null,msg[1],"Error",JOptionPane.ERROR_MESSAGE);      
        repaint();
        treeModel.reload(rootNode);              
        
        String bads=new String("");
        for(int i=0;i<kidCount;i++){
          if(!(lob[i].isValid)){
            if(!(badLink.contains(lob[i])))
              badLink.addElement(lob[i]);
            bads+=(lob[i].toString()+"\n");
          }
        }
        
        BadDialog bd=new BadDialog(parentFrame,bads);
        bd.setLocation(dim.width/4,dim.height/4);
        bd.show();
        
        int send=JOptionPane.showConfirmDialog
          (null,msg[2],"",JOptionPane.YES_NO_OPTION);
        repaint();
        treeModel.reload(rootNode);                  
        if(send==JOptionPane.YES_OPTION){
          numBad=badLink.size();
          LinkObj[] lnk=new LinkObj[numBad];
          int numToMail=0;
          for(int i=0;i<numBad;i++){
            lnk[i]=(LinkObj)badLink.elementAt(i);
            if((lnk[i].servHits)==0) numToMail++;
          }
          
          if(numToMail==0){
            JOptionPane.showMessageDialog
              (null,msg[3],"",JOptionPane.ERROR_MESSAGE);
            repaint();
            treeModel.reload(rootNode);                    
            return;
          }
          
          int ibl=0;
          String[] bl=new String[numToMail];
          for(int i=0;i<numBad;i++){
            if((lnk[i].servHits)>0) continue;
            else{
              bl[ibl]=lnk[i].toString();
              ibl++;
            }
          }
          
          boolean sendOK=sendMail(bl);
          if(sendOK){
            for(int i=0;i<numBad;i++){
              if((lnk[i].servHits)==0){
                lnk[i].servHits=1;
                badLink.removeElementAt(i);
                badLink.insertElementAt(lnk[i],i);
              }
            }
          }
        }
      }
    }
  }  
  
// This class is invoked when user wants to check selected link

  class CheckL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      String[] msg={"Error transferring data",
        "Link is OK. Do you want to open it?",
        "Link is not valid","Send error message to the owner?",
        "Select the link"};
      
      selectLink=null;
      DefaultMutableTreeNode nod=getSelectedNode();
      if((nod==null)||(nod==rootNode)){
        JOptionPane.showMessageDialog
          (null,msg[4],"Error",JOptionPane.ERROR_MESSAGE);
        repaint();
        treeModel.reload(rootNode);
        return;
      }                
      LinkObj lnk=(LinkObj)nod.getUserObject();
      if(badLink.contains(lnk)){
        JOptionPane.showMessageDialog
          (null,msg[2],"Error",JOptionPane.ERROR_MESSAGE);
        repaint();
        treeModel.reload(rootNode);        
        return;
      }        
      CheckLink cl=new CheckLink(lnk);
      
      if(!(lnk.isChecked)){
        Thread ct=new Thread(cl);
        ct.start();
        try{
          ct.join();
        }
        catch(InterruptedException e1){
          JOptionPane.showMessageDialog
            (null,msg[0],"Error",JOptionPane.ERROR_MESSAGE);
          repaint();
          treeModel.reload(rootNode);                  
          return;
        }
        lnk.isChecked=true;
      }

      if(lnk.isValid){
        int open=JOptionPane.showConfirmDialog
          (null,msg[1],"",JOptionPane.YES_NO_OPTION);
        repaint();
        treeModel.reload(rootNode);                
        if(open==JOptionPane.NO_OPTION){
          repaint();
          treeModel.reload(rootNode);
          return;
        }
        else{
          String err;
          String newPage=lnk.toString();
          threadCase=0;
          OpenThread ot=new OpenThread(newPage);
          ot.start();
          try{
            ot.join();
          }
          catch(InterruptedException e1){
            threadCase=5;
          }
          
          switch(threadCase){
            default:
            case 0: selectLink=lnk.toString(); return;
            case 1:
            case 2: err=msg[0]; break;
            case 3: err="This is not HTML document"; break;
            case 4:
            case 5: err=msg[0]; break;
          }
          JOptionPane.showMessageDialog
            (null,err,"Error",JOptionPane.ERROR_MESSAGE);
          repaint();
          treeModel.reload(rootNode);                    
        }
      }
      else{
        JOptionPane.showMessageDialog
          (null,msg[2],"Error",JOptionPane.ERROR_MESSAGE);
        repaint();
        treeModel.reload(rootNode);                
        if(lnk.servHits==0){
          int send=JOptionPane.showConfirmDialog
            (null,msg[3],"",JOptionPane.YES_NO_OPTION);
          repaint();
          treeModel.reload(rootNode);                    
          if(send==JOptionPane.YES_OPTION){
            String[] bl=new String[1];
            bl[0]=lnk.toString();
            boolean sendOK=sendMail(bl);
            if(sendOK) lnk.servHits=1;
          }
          badLink.addElement(lnk);
        }      
      }
    }
  }  
  

//////////////////////////////////////////////////////////////////////////////
  class BrowseLL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      String[] msg={"Select the link","Link is not valid",
        "Link is not checked"};

      if(selectLink !=null){
        HtmlPanel hp=new HtmlPanel(selectLink);
        hp.setSize(800,400);
        hp.setVisible(true);    
        Thread t=new Thread(hp);
        t.start();
        selectLink=null;
        return;      
      }
    
      else{
        DefaultMutableTreeNode nod=getSelectedNode();
        if((nod==null)||(nod==rootNode)){
          JOptionPane.showMessageDialog
            (null,msg[0],"Error",JOptionPane.ERROR_MESSAGE);
          repaint();
          treeModel.reload(rootNode);        
          return;
        }                
        LinkObj lnk=(LinkObj)nod.getUserObject();
        if(badLink.contains(lnk)){
          JOptionPane.showMessageDialog
            (null,msg[1],"Error",JOptionPane.ERROR_MESSAGE);
          repaint();
          treeModel.reload(rootNode);        
          return;
        }        

        if(!(lnk.isChecked)){
          JOptionPane.showMessageDialog
            (null,msg[2],"Error",JOptionPane.ERROR_MESSAGE);
          repaint();
          treeModel.reload(rootNode);        
          return;
        }
        HtmlPanel hp=new HtmlPanel(lnk.toString());
        hp.setSize(800,400);
        hp.setVisible(true);    
        Thread t=new Thread(hp);
        t.start();
        selectLink=null;
      }
    }
  }      

////////////////////////////////////////////////////////////
  class AddrL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      int kidCount=rootNode.getChildCount();
      LinkObj[] lob=new LinkObj[kidCount];
      DefaultMutableTreeNode[] nod=new DefaultMutableTreeNode[kidCount];
      for(int i=0;i<kidCount;i++){
        nod[i]=(DefaultMutableTreeNode)rootNode.getChildAt(i);
        lob[i]=(LinkObj)nod[i].getUserObject();
      }
      String addr=new String("");
      int l;
      for(int i=0;i<kidCount;i++){
        String lnk=new String(lob[i].toString());
        if((l=lnk.indexOf("mailto:"))>=0){
          String slnk=lnk.substring(l+7);
          addr+=(slnk+"\n");
        }
      }
        
      MailDialog md=new MailDialog(parentFrame,addr);
      md.setLocation(dim.width/2,dim.height/2);
      md.show();
    }
  }
////////////////////////////////////////////////////////////

// This class is invoked when user wants to send e-mail
// to the owner of the page to make her aware about invalid 
// links in the page

  class SendL implements ActionListener{
    public void actionPerformed(ActionEvent e){
      String[] msg={"Sorry, all links are valid",
        "Sorry, the Owner has already been notified"};
      
      if(badLink.isEmpty()){
        JOptionPane.showMessageDialog
          (null,msg[0],"",JOptionPane.ERROR_MESSAGE);
        repaint();
        treeModel.reload(rootNode);                
        return;
      }        
      
      int numBad=badLink.size();
      LinkObj[] lnk=new LinkObj[numBad];
      int numToMail=0;
      for(int i=0;i<numBad;i++){
        lnk[i]=(LinkObj)badLink.elementAt(i);
        if((lnk[i].servHits)==0) numToMail++;
      }
          
      if(numToMail==0){
        JOptionPane.showMessageDialog
          (null,msg[1],"",JOptionPane.ERROR_MESSAGE);
        repaint();
        treeModel.reload(rootNode);               
        return;
      }
          
      int ibl=0;
      String[] bl=new String[numToMail];
      for(int i=0;i<numBad;i++){
        if((lnk[i].servHits)>0) continue;
        else{
          bl[ibl]=lnk[i].toString();
          ibl++;
        }
      }
          
      boolean sendOK=sendMail(bl);
      if(sendOK){
        for(int i=0;i<numBad;i++){
          if((lnk[i].servHits)==0){
            lnk[i].servHits=1;
            badLink.removeElementAt(i);
            badLink.insertElementAt(lnk[i],i);
          }
        }
      }
    }
  }    
  
// This is static method that instantiates class LinkChecker
// and makes it run

  public static void main(String[] args){
    LinkChecker lChecker=new LinkChecker();
    lChecker.addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(0);
        }
      });
    lChecker.setSize(dim.width,dim.height);
    lChecker.show();
  }
} ///:~
