/*
 * HTMLDocumentLinks.java  1.0
 *
 * Copyright (c) 1999 Emmanuel PUYBARET - eTeks.
 * All Rights Reserved.
 *
 */

import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.text.*;
import javax.swing.text.html.*;

// Classe derivee de HTMLDocument pour permettre de specifier
// un reader different dans la methode getReader ()
public class HTMLDocumentLinks extends HTMLDocument
{
  // Ensemble des ancres de ce fichier HTML
  private Vector anchors = new Vector ();
  // Ensemble des liens URLs trouves dans ce fichier 
  private Vector urls    = new Vector ();
  // Ensemble des URLs mal ecrites dans ce fichier
  private Vector malformedURLs = new Vector ();

  // Constructeur
  public HTMLDocumentLinks (URL file)
  {
    // Memorisation de la base du fichier HTML
    setBase (file);
  }

  public final Vector getAnchors ()
  {
    return anchors;
  }

  public final Vector getURLs ()
  {
    return urls;
  }

  public final Vector getMalformedURLs ()
  {
    return malformedURLs;
  }

  private void addAnchor (String anchor)
  {
    // Ajout de l'ancre a l'ensemble des ancres
    anchors.addElement (anchor);
  }

  private boolean ignoreURL (String urlString)
  {
    // Ajout uniquement des URLs relatives a ce fichier
    // (les fichiers sans protocole defini avec ':').
    // Eventuellement vous pouvez filtrer differemment les
    // URLs (pas de cgi par exemple,...)
    return urlString.indexOf (':') >= 0;
  }

  private void addURL (String urlString)
  {
    if (!ignoreURL (urlString))
      try
      {
        // Fabrication de l'URL relative a ce fichier
        // pour l'ajouter a l'ensemble des URLs qu'il reference
        URL url = new URL (getBase (), urlString);
        // Sous Java 1.2.1, bug sur la construction des URLs
        // si urlString commence par # avec le protocole file 
        // => construction differente de l'URL dans ce cas
        if (urlString.startsWith ("#"))
          url = new URL (getBase ().toString () + urlString);
          
        if (!urls.contains (url))
          urls.addElement (url);
      }
      catch (MalformedURLException e)
      {
        malformedURLs.addElement (urlString);
      }
  }

  // Methode outrepassee pour founir un reader different
  public HTMLEditorKit.ParserCallback getReader (int pos)
  {
    return new LinkReader ();
  }

  // Les methodes de cette classe sont rappelees par
  // le parser HTML suivant les differents tag HTML lus.
  // Ici, le but recherche est de garder une trace de tous
  // les tags qui font appels a des URLs (fichiers HTML, images,...).
  private class LinkReader extends HTMLEditorKit.ParserCallback
  {
    // Methode appelee quand un tag de debut est rencontre
    public void handleStartTag (HTML.Tag tag, MutableAttributeSet att, int pos)
    {
      String attribute;
      if (tag.equals (HTML.Tag.A))  // Tags <A NAME=...> ou <A HREF=...>
      {
        attribute = (String)att.getAttribute (HTML.Attribute.NAME);
        if (attribute != null)
          addAnchor (attribute);
        else
        { 
          attribute = (String)att.getAttribute (HTML.Attribute.HREF);
          if (attribute != null)
            addURL (attribute);
        }
      }
      else if (tag.equals (HTML.Tag.APPLET))  // Tag <APPLET CODE=...>
      {
        attribute = (String)att.getAttribute (HTML.Attribute.CODE);
        if (attribute != null)
        {
          String archive = (String)att.getAttribute (HTML.Attribute.ARCHIVE);
          if (archive != null)
            // Branchement sur le fichier d'archive s'il existe
            attribute = archive;
          else
            if (!attribute.endsWith (".class"))
              attribute += ".class";

          // Recherche du repertoire eventuel relatif a l'applet
          String codebase = (String)att.getAttribute (HTML.Attribute.CODEBASE);
          if (codebase != null)
            if (codebase.endsWith ("/"))
              attribute = codebase + attribute;
            else
              attribute = codebase + '/' + attribute;
          addURL (attribute);
        }
      }
      else if (   tag.equals (HTML.Tag.BODY)  // Tag <BODY BACKGROUND=...>
               || tag.equals (HTML.Tag.TABLE) // Tag <TABLE BACKGROUND=...>
               || tag.equals (HTML.Tag.TR)    // Tag <TR BACKGROUND=...>
               || tag.equals (HTML.Tag.TD))   // Tag <TD BACKGROUND=...>
      {
        attribute = (String)att.getAttribute (HTML.Attribute.BACKGROUND);
        if (attribute != null)
          addURL (attribute);
      }
    }

    // Methode appelee quand un tag simple est lu
    public void handleSimpleTag (HTML.Tag tag, MutableAttributeSet att, int pos)
    {
      String    attribute;
      if (   tag.equals (HTML.Tag.FRAME) // Tag <FRAME SRC=...>
          || tag.equals (HTML.Tag.IMG))  // Tag <IMG SRC=...>
      {
        attribute = (String)att.getAttribute (HTML.Attribute.SRC);
        if (attribute != null)
          addURL (attribute);
      }
      else if (tag.equals (HTML.Tag.AREA)) // Tag <AREA HREF=...>
      {
        attribute = (String)att.getAttribute (HTML.Attribute.HREF);
        if (attribute != null)
          addURL (attribute);
      }
    }

    // HTMLEditorKit.ParserCallback definit aussi d'autres methodes
    // qu'il est inutile d'outrepasser ici (tag de fin, commentaires, 
    // corps de texte,...)
  }

  // Methode main () d'exemple de mise en oeuvre, prend en argument 
  // un nom de fichier HTML sous forme d'URL, par exemple :
  // java HTMLDocumentLinks file:/disk/dir1/index.html
  public static void main (String args [])
  {
    try
    {
      // Ouverture du fichier contenu dans l'argument 0
      URL    fileURL = new URL (args [0]);
      Reader urlReader = new BufferedReader (
                           new InputStreamReader (fileURL.openStream ()));

      // Creation d'une instance de parser
      HTMLDocumentLinks doc = new HTMLDocumentLinks (fileURL);

      // Parsing du fichier HTML avec Swing
      new HTMLEditorKit ().read (urlReader, doc, 0);
      urlReader.close ();

      // Listing des ancres et des URLs trouvees dans le fichier
      System.out.println ("Liste des ancres :");
      for (Enumeration e = doc.getAnchors ().elements ();
           e.hasMoreElements (); )
        System.out.println (e.nextElement ());

      System.out.println ("\nListe des URLs :");
      for (Enumeration e = doc.getURLs ().elements ();
           e.hasMoreElements (); )
        System.out.println (e.nextElement ());
    }
    catch (IOException e)
    {
      System.out.println ("Probleme d'acces a l'URL : " + args [0]);
    }
    catch (BadLocationException e)
    { }
  }
}
