1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.exec;
  28 
  29 import com.sun.javatest.tool.UIFactory;
  30 import com.sun.javatest.report.HTMLWriterEx;
  31 
  32 import java.awt.*;
  33 import java.awt.event.ActionEvent;
  34 import java.awt.event.ActionListener;
  35 import java.awt.print.PageFormat;
  36 import java.awt.print.Printable;
  37 import java.io.*;
  38 import java.net.MalformedURLException;
  39 import java.net.URL;
  40 import java.nio.charset.StandardCharsets;
  41 import java.util.HashMap;
  42 import java.util.Iterator;
  43 import javax.imageio.ImageIO;
  44 import javax.imageio.stream.ImageInputStream;
  45 import javax.sound.midi.InvalidMidiDataException;
  46 import javax.sound.midi.MidiFileFormat;
  47 import javax.sound.midi.MidiSystem;
  48 import javax.sound.midi.Sequence;
  49 import javax.sound.midi.Sequencer;
  50 import javax.sound.sampled.AudioFileFormat;
  51 import javax.sound.sampled.AudioFormat;
  52 import javax.sound.sampled.AudioInputStream;
  53 import javax.sound.sampled.AudioSystem;
  54 import javax.sound.sampled.Clip;
  55 import javax.sound.sampled.DataLine;
  56 import javax.sound.sampled.LineEvent;
  57 import javax.sound.sampled.LineListener;
  58 import javax.sound.sampled.UnsupportedAudioFileException;
  59 import javax.swing.*;
  60 import javax.swing.event.HyperlinkEvent;
  61 import javax.swing.event.HyperlinkListener;
  62 import javax.swing.text.*;
  63 import javax.swing.text.html.HTML;
  64 import javax.swing.text.html.HTMLDocument;
  65 import javax.swing.text.html.HTMLEditorKit;
  66 import javax.swing.text.html.HTMLFrameHyperlinkEvent;
  67 import javax.swing.text.rtf.RTFEditorKit;
  68 
  69 /**
  70  * A panel to display files which may be linked together by hyperlinks.
  71  * This panel can also display a list of local files.  The type of the file
  72  * to be shown is identified if possible and the display panel put into the
  73  * right mode to show an image, rendered HTML, etc.  If the file is not known,
  74  * the user may be prompted to display the file as raw text.
  75  */
  76 public class MultiFormatPane extends JPanel implements Printable {
  77 
  78     public MultiFormatPane(UIFactory uif) {
  79         this.uif = uif;
  80 
  81         listener = new Listener();
  82         initGUI();
  83 
  84         modesToPanes = new HashMap();
  85 
  86         addMediaPane(TEXT, textPane);
  87         addMediaPane(AUDIO, musicPane);
  88         addMediaPane(IMAGE, imagePane);
  89         addMediaPane(ERROR, errorPane);
  90 
  91     }
  92 
  93     public void setNoteField(JTextField noteField) {
  94         this.noteField = noteField;
  95     }
  96 
  97     public void setNavigationPane(NavigationPane navPane) {
  98         this.navPane = navPane;
  99     }
 100 
 101     public void addMediaPane(int mode, MediaPane pane) {
 102         modesToPanes.put(mode, pane);
 103     }
 104 
 105     public MediaPane getMediaPane(int mode) {
 106         return (MediaPane)modesToPanes.get(mode);
 107     }
 108 
 109     public void clear() {
 110         currURL = null;
 111         if(navPane != null) {
 112             navPane.clear();
 113         }
 114         musicPane.stopAudio();
 115     }
 116 
 117     public URL getPage() {
 118         return currURL;
 119     }
 120 
 121     public int getCurrentMode() {
 122         return getCurrentPane().getMode();
 123     }
 124 
 125     public MediaPane getCurrentPane() {
 126         return (MediaPane)scrllBody.getViewport().getComponent(0);
 127     }
 128 
 129     // Base directory is root directory for displayable content
 130     // if it set, all filenames will be relative to this directory
 131     void setBaseDirectory(File base) {
 132         baseDir = base;
 133     }
 134 
 135     File getBaseDirectory() {
 136         return baseDir;
 137     }
 138 
 139     void setDefaultView() {
 140         textPane.setDocument(new HTMLDocument());
 141         textPane.showTextArea();
 142     }
 143 
 144     private void initGUI() {
 145         uif.initPanel(this, "mfp", false);
 146 
 147         textPane = new TextPane(uif, "mfp.textPane", null);
 148         textPane.addHyperlinkListener(listener);
 149 
 150         scrllBody = uif.createScrollPane(textPane,
 151                                  JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
 152                                  JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 153 
 154         textPane.setParent(scrllBody);
 155 
 156         musicPane = new MusicPane(uif, "mfp.audioPane", scrllBody);
 157         imagePane = new ImagePane(uif, "mfp.imagePanelbl", scrllBody);
 158         errorPane = new ErrorFormatPane(uif, "mfp.errorPane", scrllBody);
 159 
 160         scrllBody.setViewportView(textPane);
 161 
 162         setLayout(new BorderLayout());
 163 
 164         add(scrllBody, BorderLayout.CENTER);
 165 
 166     }
 167 
 168 
 169     //Loads everything by URL
 170     public void loadPage(URL url) {
 171         // avoid recursive callbacks from updating combo
 172         // URL.equals can result in a big performance hit
 173         if (currURL != null && url.toString().equals(currURL.toString()))
 174             return;
 175 
 176         currURL = url;
 177 
 178         String protocol = url.getProtocol();
 179         File file = new File(url.getFile());
 180         if (protocol.equals("https")) {
 181             // JavaTestSecurityManager disallows Permission getProperty.ssl.SocketFactory.provider
 182             // so https can't be processed
 183             openInBrowser(url);
 184 
 185         } else if (protocol.equals("file") && file.isDirectory()) {
 186             String list = listLocalDirectory(file);
 187             textPane.showText(list, "text/html");
 188             ((HTMLDocument)textPane.getDocument()).setBase(url);
 189         }
 190         else if (ImagePane.isImageResource(url)) {
 191             imagePane.showImage(url);
 192         }
 193         else if(MusicPane.isAudioResource(url)) {
 194             musicPane.showAudio(url);
 195         }
 196         else if(isMediaResourceCorrupted(url)) {
 197             errorPane.showError(uif.getI18NString("mfp.errorPaneLbl.corrupted"));
 198         }
 199         else if(TextPane.isTextResource(url)){
 200             textPane.showText(url);
 201         }
 202         else if (url.getProtocol().equalsIgnoreCase("http")) {
 203             textPane.showText(url);
 204         }
 205         else {
 206             int response = uif.showYesNoDialog("mfp.unknownTypeDlg");
 207             if(response == JOptionPane.YES_OPTION) {
 208                 textPane.showText(url);
 209             }
 210             else {
 211                 errorPane.showError(uif.getI18NString("mfp.errorPaneLbl.unsupported"));
 212             }
 213         }
 214 
 215         if(navPane != null) {
 216             navPane.processURL(url);
 217         }
 218     }
 219 
 220     private void openInBrowser(URL url) {
 221         Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
 222         if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
 223             try {
 224                 desktop.browse(url.toURI());
 225             } catch (Exception e) {
 226                 e.printStackTrace();
 227             }
 228         }
 229     }
 230 
 231     public void showText(String text, String MIME) {
 232         textPane.showText(text, MIME);
 233         currURL = null;
 234     }
 235 
 236     public void showText(URL url) {
 237         textPane.showText(url);
 238     }
 239 
 240     public void showAudio(URL url) {
 241         musicPane.showAudio(url);
 242     }
 243 
 244     public void stopAudio() {
 245         musicPane.stopAudio();
 246     }
 247 
 248     public void showImage(URL url) {
 249         imagePane.showImage(url);
 250     }
 251 
 252     public void showError(String errorMessage) {
 253         errorPane.showError(errorMessage);
 254     }
 255 
 256 
 257     private boolean isMediaResourceSupported(URL url) {
 258         return ImagePane.isImageResource(url) || MusicPane.isAudioResource(url);
 259     }
 260 
 261     private boolean isMediaResourceCorrupted(URL url) {
 262         String filename = url.getFile();
 263         String ext = filename.substring(filename.lastIndexOf('.') + 1);
 264         ext = ext.toLowerCase();
 265 
 266         return (SUPPORTED_MEDIA_RESOURCE_EXTENSIONS.indexOf(ext) != -1) &&
 267                 !isMediaResourceSupported(url);
 268     }
 269 
 270 
 271     private String listLocalDirectory(File dir) {
 272         if (!dir.isAbsolute())
 273             dir = dir.getAbsoluteFile();
 274 
 275         String displayPath = dir.getPath();
 276         // if contains base dir, only show path relative to baseDir
 277         if (baseDir != null) {
 278             String p = baseDir.getParent();
 279             if (p != null
 280                 && displayPath.startsWith(p) &&
 281                 (displayPath.length() > p.length())) {
 282                 displayPath = displayPath.substring(p.length());
 283                 // in case of Unix
 284                 if (displayPath.startsWith(File.separator)) {
 285                     displayPath = displayPath.substring(1);
 286                 }
 287             }
 288         }
 289 
 290         String[] filelist = dir.list();
 291 
 292         StringWriter sw = new StringWriter();
 293         try {
 294             HTMLWriterEx out = new HTMLWriterEx(sw, uif.getI18NResourceBundle());
 295 
 296             out.startTag(HTMLWriterEx.HTML);
 297             out.startTag(HTMLWriterEx.HEAD);
 298             out.writeContentMeta();
 299             out.startTag(HTMLWriterEx.TITLE);
 300             out.write(displayPath);
 301             out.endTag(HTMLWriterEx.TITLE);
 302             out.endTag(HTMLWriterEx.HEAD);
 303             out.startTag(HTMLWriterEx.BODY);
 304             out.writeStyleAttr("font-family: SansSerif; font-size: 12pt");
 305             out.startTag(HTMLWriterEx.H3);
 306             out.writeI18N("mfp.head", displayPath);
 307             out.endTag(HTMLWriterEx.H3);
 308             out.startTag(HTMLWriterEx.UL);
 309             out.writeStyleAttr("margin-left:0");
 310 
 311             File parent = dir.getParentFile();
 312             if (parent != null) {
 313                 out.startTag(HTMLWriterEx.LI);
 314                 out.startTag(HTMLWriterEx.OBJECT);
 315                 out.writeAttr(HTMLWriterEx.CLASSID, "com.sun.javatest.tool.IconLabel");
 316                 out.writeParam("type", "up");
 317                 out.endTag(HTMLWriterEx.OBJECT);
 318                 out.writeEntity(" ");
 319                 try {
 320                     out.startTag(HTMLWriterEx.A);
 321                     out.writeAttr(HTMLWriterEx.HREF, parent.toURL().toString());
 322                     out.writeI18N("mfp.parent");
 323                     out.endTag(HTMLWriterEx.A);
 324                 }
 325                 catch (MalformedURLException e) {
 326                     out.writeI18N("mfp.parent");
 327                 }
 328             }
 329 
 330             for (int i = 0; i < filelist.length; i++) {
 331                 File file = new File(dir, filelist[i]);
 332                 out.startTag(HTMLWriterEx.LI);
 333                 out.startTag(HTMLWriterEx.OBJECT);
 334                 out.writeAttr(HTMLWriterEx.CLASSID, "com.sun.javatest.tool.IconLabel");
 335                 out.writeParam("type", (file.isDirectory() ? "folder" : "file"));
 336                 out.endTag(HTMLWriterEx.OBJECT);
 337                 out.writeEntity("&nbsp;");
 338                 try {
 339                     out.writeLink(file.toURL(), file.getName());
 340                 }
 341                 catch (MalformedURLException e) {
 342                     out.write(file.getName());
 343                 }
 344             }
 345 
 346             out.endTag(HTMLWriterEx.UL);
 347             out.endTag(HTMLWriterEx.BODY);
 348             out.endTag(HTMLWriterEx.HTML);
 349             out.close();
 350         }
 351         catch (IOException e) {
 352             // should not happen, writing to StringWriter
 353         }
 354 
 355         return sw.toString();
 356     }
 357 
 358     public interface MediaPane {
 359 
 360         public int getMode();
 361 
 362         public void changeURL(URL url);
 363 
 364         public void setParent(JScrollPane pane);
 365     }
 366 
 367 // Print this panel
 368     public int print (Graphics g, PageFormat pf, int pageIndex) {
 369 
 370         int response = NO_SUCH_PAGE;
 371 
 372         Graphics2D g2 = (Graphics2D) g;
 373 
 374         Component componentToBePrinted = scrllBody.getComponent(0);
 375         int mode = getCurrentMode();
 376         switch(mode) {
 377             case TEXT:
 378                 componentToBePrinted = textPane;
 379                 break;
 380             case IMAGE:
 381                 componentToBePrinted = imagePane;
 382                 break;
 383             default:
 384                 break;
 385         }
 386 
 387         //  for faster printing, turn off double buffering
 388         disableDoubleBuffering(componentToBePrinted);
 389 
 390         Dimension d = componentToBePrinted.getSize();
 391         double panelWidth = d.width;
 392         double panelHeight = d.height;
 393 
 394         double pageHeight = pf.getImageableHeight();
 395         double pageWidth = pf.getImageableWidth();
 396 
 397         double scale = pageWidth / panelWidth;
 398         int totalNumPages = (int) Math.ceil(scale * panelHeight / pageHeight);
 399 
 400         if (pageIndex >= totalNumPages) {
 401           response = NO_SUCH_PAGE;
 402         }
 403         else {
 404 
 405           //  shift Graphic to line up with beginning of print-imageable region
 406           g2.translate(pf.getImageableX(), pf.getImageableY());
 407 
 408           //  shift Graphic to line up with beginning of next page to print
 409           g2.translate(0f, -pageIndex * pageHeight);
 410 
 411           g2.scale(scale, scale);
 412 
 413           componentToBePrinted.paint(g2);
 414 
 415           enableDoubleBuffering(componentToBePrinted);
 416           response = Printable.PAGE_EXISTS;
 417         }
 418         return response;
 419     }
 420 
 421     private void disableDoubleBuffering(Component c) {
 422         RepaintManager currentManager = RepaintManager.currentManager(c);
 423         currentManager.setDoubleBufferingEnabled(false);
 424       }
 425 
 426     private void enableDoubleBuffering(Component c) {
 427         RepaintManager currentManager = RepaintManager.currentManager(c);
 428         currentManager.setDoubleBufferingEnabled(true);
 429     }
 430 // END of printing
 431 
 432     private class Listener implements HyperlinkListener {
 433         public void hyperlinkUpdate(HyperlinkEvent e) {
 434             HyperlinkEvent.EventType et = e.getEventType();
 435             if (et == HyperlinkEvent.EventType.ACTIVATED) {
 436                 if (e instanceof HTMLFrameHyperlinkEvent) {
 437 //                  HTMLDocument doc = (HTMLDocument)
 438 //                      ((JEditorPane) e.getSource()).getDocument();
 439 //                  doc.processHTMLFrameHyperlinkEvent((HTMLFrameHyperlinkEvent) e);
 440                     processFrameLink(e);
 441                 }
 442                 else {
 443                     musicPane.stopAudio();
 444 //                    String s = e.getDescription();
 445 //                    URL correctURL = e.getURL();
 446 //                    HTMLDocument doc = (HTMLDocument)((JEditorPane)e.getSource()).getDocument();
 447 //                    if(doc != null && currURL != null && !doc.getBase().equals(currURL))
 448 //                        currURL = doc.getBase();
 449 //                    // JEditorPane doesn't allows javascript, so we delete this parameter and load
 450 //                    // root page of help package
 451 //                    if(correctURL.getProtocol().equals("file")) {
 452 
 453 //                        int pos = correctURL.toString().indexOf("?");
 454 //                        if (pos >= 0) {
 455 //                            try {
 456 //                                correctURL = new URL(correctURL.toString().substring(0, pos));
 457 //                            } catch (MalformedURLException ex) {
 458 //                                // NONE
 459 //                            }
 460 //                        }
 461 //                    }
 462 //                    // end of fix
 463 //                    loadPage(correctURL);
 464                     if(e.getURL() == null && e.getDescription().startsWith("#")) {
 465                         processSharpLink(e);
 466                     }
 467                     else {
 468                         processGeneralLink(e);
 469                     }
 470                 }
 471             }
 472             else if (et == HyperlinkEvent.EventType.ENTERED) {
 473                 URL u = e.getURL();
 474                 if (u != null) {
 475                     textPane.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
 476                     if(noteField != null) {
 477                         noteField.setText(u.toString());
 478                     }
 479                 }
 480             }
 481             else if (et == HyperlinkEvent.EventType.EXITED) {
 482                 textPane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 483                 if(noteField != null) {
 484                     noteField.setText("");
 485                 }
 486             }
 487         }
 488 
 489         private void processFrameLink(HyperlinkEvent e) {
 490             HTMLDocument doc = (HTMLDocument)
 491                 ((JEditorPane) e.getSource()).getDocument();
 492             doc.processHTMLFrameHyperlinkEvent((HTMLFrameHyperlinkEvent) e);
 493         }
 494 
 495         private void processSharpLink(HyperlinkEvent e) {
 496             String desc = e.getDescription();
 497             if (!(desc.startsWith("#"))) {
 498                 return;
 499             }
 500             textPane.scrollToReference(desc.substring(1));
 501         }
 502 
 503         private void processGeneralLink(HyperlinkEvent e) {
 504             URL correctURL = e.getURL();
 505 
 506             if (correctURL == null) {
 507                 return;
 508             }
 509 
 510             HTMLDocument doc = (HTMLDocument)((JEditorPane)e.getSource()).getDocument();
 511             if(doc != null && currURL != null && !doc.getBase().toString().equals(currURL.toString())) {
 512                 currURL = doc.getBase();
 513             }
 514             // fix of CR 6451318, CR 6442782 and CR 6447246
 515             // JDTS use '?' notation to pass parameter (currently loaded page of one of frames)
 516             // to javascript.
 517             // JEditorPane doesn't allows javascript, so we delete this parameter and load
 518             // root page of help package
 519             if(correctURL.getProtocol().equals("file")) {
 520                 int pos = correctURL.toString().indexOf("?");
 521                 if (pos >= 0) {
 522                     try {
 523                         correctURL = new URL(correctURL.toString().substring(0, pos));
 524                     } catch (MalformedURLException ex) {
 525                         // NONE
 526                     }
 527                 }
 528             }
 529             // end of fix
 530             loadPage(correctURL);
 531         }
 532     }
 533 
 534 
 535 
 536 
 537     private static final String SUPPORTED_MEDIA_RESOURCE_EXTENSIONS =
 538             ".wav .au .aif .mid .midi .rmf .jpeg .jpg .gif .png";
 539     private JScrollPane scrllBody;
 540     public static final int TEXT = 0;
 541     public static final int IMAGE = 1;
 542     public static final int AUDIO = 2;
 543     public static final int ERROR = 3;
 544 
 545     private File baseDir;
 546     private URL currURL;
 547 
 548     private TextPane textPane;
 549     private MusicPane musicPane;
 550     private ImagePane imagePane;
 551     private ErrorFormatPane errorPane;
 552 
 553     private UIFactory uif;
 554     private String uiKey;
 555 
 556     private Listener listener;
 557 
 558     private JTextField noteField;
 559     private NavigationPane navPane;
 560 
 561     private HashMap modesToPanes;
 562 }
 563 
 564 
 565 class TextPane extends JEditorPane implements MultiFormatPane.MediaPane {
 566 
 567     public TextPane(UIFactory uif, String uiKey, JScrollPane owner) {
 568         super();
 569 
 570         this.uif = uif;
 571         this.owner = owner;
 572 
 573         setName("text");
 574         setEditable(false);
 575         setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 576         setFont(new Font(Font.MONOSPACED, uif.getBaseFont().getStyle(), uif.getBaseFont().getSize()));
 577 
 578         uif.setAccessibleInfo(this, uiKey);
 579 
 580         htmlKit = new HTMLEditorKit();
 581         defaultKit = new DefaultEditorKit();
 582         rtfKit = new RTFEditorKit();
 583 
 584     }
 585 
 586     public void changeURL(URL url) {}
 587 
 588     public void setParent(JScrollPane owner) {
 589         this.owner = owner;
 590     }
 591 
 592     public int getMode() {
 593         return MultiFormatPane.TEXT;
 594     }
 595 
 596     public void showText(String text, String MIME) {
 597         setContentType(MIME);
 598 
 599         EditorKit kit = getKitByMIME(MIME);
 600         Document doc = kit.createDefaultDocument();
 601 
 602         setDocument(doc);
 603 
 604         setText(text);
 605 
 606         showTextArea();
 607     }
 608 
 609     public void showText(URL url) {
 610         try {
 611             String mimeType = getMIMEType(url);
 612             if (mimeType == null) {
 613                 mimeType = "text/plain";
 614             }
 615 
 616             boolean loadManually = false;
 617             if (mimeType.indexOf("plain") != -1) {
 618                 loadManually = true;
 619             }
 620 
 621             EditorKit kit = getKitByMIME(mimeType);
 622 
 623             setContentType(mimeType);
 624 
 625             Document doc = kit.createDefaultDocument();
 626             setDocument(doc);
 627             if (loadManually  && "file".equals(url.getProtocol())) {
 628                 File file = new File(url.getFile());
 629                 try {
 630                     Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
 631                     read(r, url);
 632                     r.close();
 633                 }
 634                 catch (IOException e) {
 635                     uif.showError("mfp.load.error", new Object[] { url, e });
 636                 }
 637             } else {
 638                 setPage(url);
 639             }
 640 
 641             showTextArea();
 642         }
 643         catch (IOException e) {
 644             uif.showError("mfp.load.error", new Object[] { url, e });
 645         }
 646     }
 647 
 648     public static boolean isTextResource(URL url) {
 649         String mimeType = getMIMEType(url);
 650         if (mimeType == null)
 651             return false;
 652         return (mimeType.equals("text/plain") || mimeType.equals("text/html") ||
 653                 mimeType.equals("text/rtf"));
 654     }
 655 
 656     public static String getMIMEType(URL url) {
 657         String filename = url.getFile();
 658         String ext = filename.substring(filename.lastIndexOf('.') + 1);
 659         ext = ext.toLowerCase();
 660 
 661         return (String)extensionsToMIME.get(ext);
 662     }
 663 
 664     public void showTextArea() {
 665         owner.setViewportView(this);
 666         owner.revalidate();
 667         owner.repaint();
 668     }
 669 
 670     private EditorKit getKitByMIME(String mime) {
 671         if(mime.indexOf("rtf") != -1)
 672             return rtfKit;
 673         else if(mime.indexOf("html") != -1)
 674             return htmlKit;
 675         else {
 676             return defaultKit;
 677         }
 678     }
 679 
 680     public void scrollToReference(String reference) {
 681         Document d = getDocument();
 682         if (d instanceof HTMLDocument) {
 683             HTMLDocument doc = (HTMLDocument) d;
 684             HTMLDocument.Iterator iter = doc.getIterator(HTML.Tag.A);
 685             for (; iter.isValid(); iter.next()) {
 686                 AttributeSet a = iter.getAttributes();
 687                 String nm = (String) a.getAttribute(HTML.Attribute.NAME);
 688                 if (nm == null){
 689                     nm = (String) a.getAttribute(HTML.Attribute.ID);
 690                 }
 691                 if ((nm != null) && nm.equals(reference)) {
 692                     // found a matching reference in the document.
 693                     try {
 694                         int pos = iter.getStartOffset();
 695                         Rectangle r = modelToView(pos);
 696                         if (r != null) {
 697                             // the view is visible, scroll it to the
 698                             // center of the current visible area.
 699                             Rectangle vis = getVisibleRect();
 700                             //r.y -= (vis.height / 2);
 701                             r.height = vis.height;
 702                             scrollRectToVisible(r);
 703                             setCaretPosition(pos);
 704                         }
 705                     } catch (BadLocationException ble) {
 706                         UIManager.getLookAndFeel().provideErrorFeedback(TextPane.this);
 707                     }
 708                 }
 709             }
 710         }
 711     }
 712 
 713     private JScrollPane owner;
 714     private UIFactory uif;
 715 
 716     private HTMLEditorKit htmlKit;
 717     private DefaultEditorKit defaultKit;
 718     private RTFEditorKit rtfKit;
 719 
 720 
 721     private static HashMap extensionsToMIME;
 722     static {
 723         extensionsToMIME = new HashMap();
 724         extensionsToMIME.put("html", "text/html");
 725         extensionsToMIME.put("htm", "text/html");
 726         extensionsToMIME.put("htmls", "text/html");
 727         extensionsToMIME.put("htx", "text/html");
 728         extensionsToMIME.put("shtml", "text/html");
 729         extensionsToMIME.put("stm", "text/html");
 730         extensionsToMIME.put("jsp", "text/html");
 731 
 732         extensionsToMIME.put("text", "text/plain");
 733         extensionsToMIME.put("txt", "text/plain");
 734         extensionsToMIME.put("log", "text/plain");
 735         extensionsToMIME.put("list", "text/plain");
 736         extensionsToMIME.put("lst", "text/plain");
 737         extensionsToMIME.put("java", "text/plain");
 738         extensionsToMIME.put("xml", "text/plain");
 739         extensionsToMIME.put("jtr", "text/plain");
 740         extensionsToMIME.put("jti", "text/plain");
 741         extensionsToMIME.put("jtm", "text/plain");
 742         extensionsToMIME.put("jtx", "text/plain");
 743         extensionsToMIME.put("kfl", "text/plain");
 744         extensionsToMIME.put("css", "text/plain");
 745         extensionsToMIME.put("fx",  "text/plain");
 746         extensionsToMIME.put("jasm",  "text/plain");
 747         extensionsToMIME.put("jcod",  "text/plain");
 748 
 749         extensionsToMIME.put("rtf", "text/rtf");
 750         extensionsToMIME.put("rtx", "text/rtf");
 751 
 752     }
 753 
 754 
 755 }
 756 
 757 class MusicPane extends JPanel implements MultiFormatPane.MediaPane {
 758     public MusicPane(UIFactory uif, String uiKey, JScrollPane owner) {
 759         super();
 760 
 761         this.uif = uif;
 762         this.owner = owner;
 763 
 764         initGUI(uiKey);
 765     }
 766 
 767     private void initGUI(String uiKey) {
 768         setName(uiKey);
 769         uif.setAccessibleInfo(this, uiKey);
 770 
 771         setLayout(new GridBagLayout());
 772         GridBagConstraints gbc = new GridBagConstraints();
 773 
 774         btnStart = uif.createButton(uiKey + ".btnStart");
 775         btnStop = uif.createButton(uiKey + ".btnStop");
 776         btnLoop = uif.createButton(uiKey + ".btnLoop");
 777 
 778         add(btnStart, gbc);
 779         add(btnStop, gbc);
 780         add(btnLoop, gbc);
 781 
 782         btnStart.addActionListener(new ActionListener() {
 783             public void actionPerformed(ActionEvent e) {
 784                 if(isSampledAudioResource(currURL)) {
 785                     loadSample(currURL);
 786                     clip.start();
 787                 }
 788                 else if(isMidiAudioResource(currURL)) {
 789                     loadSequence(currURL);
 790                     sequencer.start();
 791                 }
 792             }
 793         });
 794 
 795         btnStop.addActionListener(new ActionListener() {
 796             public void actionPerformed(ActionEvent e) {
 797                 stopAudio();
 798             }
 799         });
 800 
 801         btnLoop.addActionListener(new ActionListener() {
 802             public void actionPerformed(ActionEvent e) {
 803                 if(isSampledAudioResource(currURL)) {
 804                     loadSample(currURL);
 805                     clip.loop(Clip.LOOP_CONTINUOUSLY);
 806                 }
 807             }
 808         });
 809 
 810     }
 811 
 812     public void showAudio(URL url) {
 813         changeURL(url);
 814 
 815         owner.setViewportView(this);
 816         owner.revalidate();
 817         owner.repaint();
 818     }
 819 
 820     public void changeURL(URL url) {
 821         currURL = url;
 822 
 823         if(isMidiAudioResource(url)) {
 824             btnLoop.setEnabled(false);
 825         }
 826         else if(isSampledAudioResource(url)) {
 827             btnLoop.setEnabled(true);
 828         }
 829 
 830     }
 831 
 832     public void setParent(JScrollPane owner) {
 833         this.owner = owner;
 834     }
 835 
 836     public int getMode() {
 837         return MultiFormatPane.AUDIO;
 838     }
 839 
 840     public static boolean isAudioResource(URL url) {
 841         return isSampledAudioResource(url) || isMidiAudioResource(url);
 842     }
 843 
 844     public void stopAudio() {
 845         if(clip != null && clip.isRunning()) {
 846             clip.stop();
 847             clip.flush();
 848             clip.close();
 849         }
 850         if(sequencer != null && sequencer.isRunning())
 851             sequencer.stop();
 852     }
 853 
 854     private void loadSample(URL url) {
 855         try {
 856             AudioInputStream stream = AudioSystem.getAudioInputStream(url);
 857             AudioFormat format = stream.getFormat();
 858 
 859             DataLine.Info info = new DataLine.Info(
 860                 Clip.class, stream.getFormat(), ((int)stream.getFrameLength()*format.getFrameSize()));
 861 
 862             clip = (Clip) AudioSystem.getLine(info);
 863             clip.open(stream);
 864             clip.addLineListener(new LineListener() {
 865                public void update(LineEvent e) {
 866                    if(e.getType() == LineEvent.Type.STOP) {
 867                        clip.stop();
 868                    }
 869                }
 870             });
 871         } catch (Exception e) {
 872             clip.close();
 873         }
 874     }
 875 
 876     private void loadSequence(URL url) {
 877         try {
 878             Sequence sequence = MidiSystem.getSequence(url);
 879 
 880             // Create a sequencer for the sequence
 881             sequencer = MidiSystem.getSequencer();
 882             sequencer.open();
 883             sequencer.setSequence(sequence);
 884 
 885         } catch (Exception e) {}
 886 
 887     }
 888 
 889     private static boolean isSampledAudioResource(URL url) {
 890         try{
 891             AudioFileFormat fformat = AudioSystem.getAudioFileFormat(url);
 892             return AudioSystem.isFileTypeSupported(fformat.getType());
 893         } catch (UnsupportedAudioFileException unsuppExc) {
 894             return false;
 895         } catch (IOException ioExc) {
 896             return false;
 897         }
 898     }
 899 
 900     private static boolean isMidiAudioResource(URL url) {
 901         try {
 902             MidiFileFormat fformat = MidiSystem.getMidiFileFormat(url);
 903             Sequence sequence = MidiSystem.getSequence(url);
 904             return MidiSystem.isFileTypeSupported(fformat.getType());
 905         } catch (InvalidMidiDataException invalidDataExc) {
 906             return false;
 907         } catch (IOException ioExc) {
 908             return false;
 909         }
 910         catch (Exception exc) {
 911             return false;
 912         }
 913     }
 914 
 915 
 916     private UIFactory uif;
 917     private JScrollPane owner;
 918 
 919     private JButton btnStart;
 920     private JButton btnStop;
 921     private JButton btnLoop;
 922 
 923     private URL currURL;
 924     private Clip clip;
 925     private Sequencer sequencer;
 926 
 927 }
 928 
 929 class ImagePane extends JLabel implements MultiFormatPane.MediaPane {
 930 
 931     public ImagePane(UIFactory uif, String uiKey, JScrollPane owner) {
 932         super(uif.getI18NString(uiKey + ".lbl"));
 933 //        setName(uiKey);
 934 
 935         this.owner = owner;
 936     }
 937 
 938     public void changeURL(URL url) {}
 939 
 940     public void setParent(JScrollPane owner) {
 941         this.owner = owner;
 942     }
 943 
 944     public int getMode() {
 945         return MultiFormatPane.IMAGE;
 946     }
 947 
 948     public void showImage(URL url) {
 949         if(isImageFormatSupported(url)) {
 950             ImageIcon img = new ImageIcon(url);
 951             setIcon(img);
 952 
 953             owner.setViewportView(this);
 954             owner.revalidate();
 955             owner.repaint();
 956         }
 957     }
 958 
 959     public static boolean isImageResource(URL url) {
 960         String file = url.getFile();
 961         String ext = file.substring(file.lastIndexOf('.')+1);
 962         Iterator iter = ImageIO.getImageReadersBySuffix(ext);
 963         return iter.hasNext();
 964     }
 965 
 966     public static boolean isImageFormatSupported(URL url) {
 967         try {
 968             ImageInputStream iis = ImageIO.createImageInputStream(new File(url.getFile()));
 969             Iterator iter = ImageIO.getImageReaders(iis);
 970             if(!iter.hasNext())
 971                 return false;
 972         } catch (IOException exc) {
 973             exc.printStackTrace();
 974             return false;
 975         }
 976         return true;
 977     }
 978 
 979     private JScrollPane owner;
 980 }
 981 
 982 class ErrorFormatPane extends JPanel implements MultiFormatPane.MediaPane {
 983     public ErrorFormatPane(UIFactory uif, String uiKey, JScrollPane owner) {
 984         super();
 985         uif.initPanel(this, uiKey, true);
 986 
 987         this.owner = owner;
 988 
 989         setLayout(new GridBagLayout());
 990         GridBagConstraints gbc = new GridBagConstraints();
 991         gbc.anchor = gbc.CENTER;
 992         errorLabel = uif.createLabel("mfp.errorPaneLbl", true);
 993         add(errorLabel, gbc);
 994 
 995     }
 996 
 997     public void changeURL(URL url) {}
 998 
 999     public void setParent(JScrollPane owner) {
1000         this.owner = owner;
1001     }
1002 
1003     public int getMode() {
1004         return MultiFormatPane.ERROR;
1005     }
1006 
1007     public void showError(String errorMessage) {
1008         errorLabel.setText(errorMessage);
1009 
1010         owner.setViewportView(this);
1011         owner.revalidate();
1012         owner.repaint();
1013     }
1014 
1015     private JLabel errorLabel;
1016     private JScrollPane owner;
1017 }