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