Package org.apache.jmeter.visualizers

Source Code of org.apache.jmeter.visualizers.ViewResultsFullVisualizer$LocalHTMLEditorKit$LocalHTMLFactory

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.apache.jmeter.visualizers;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.ToolTipManager;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.ComponentView;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.Element;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.apache.jmeter.assertions.AssertionResult;
import org.apache.jmeter.samplers.Clearable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.util.XPathUtil;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.log.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;
import org.xml.sax.SAXException;

/**
* Allows the tester to view the textual response from sampling an Entry. This
* also allows to "single step through" the sampling process via a nice
* "Continue" button.
*
* Created 2001/07/25
*/
public class ViewResultsFullVisualizer extends AbstractVisualizer
        implements ActionListener, TreeSelectionListener, Clearable
    {

    private static final Logger log = LoggingManager.getLoggerForClass();

    // Maximum size that we will display
    private static final int MAX_DISPLAY_SIZE =
        JMeterUtils.getPropDefault("view.results.tree.max_size", 200 * 1024); // $NON-NLS-1$

  // N.B. these are not multi-threaded, so don't make it static
  private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); // ISO format $NON-NLS-1$
 
  private static final String NL = "\n"; // $NON-NLS-1$

  private static final byte[] XML_PFX = "<?xml ".getBytes(); // $NON-NLS-1$

  public static final Color SERVER_ERROR_COLOR = Color.red;

  public static final Color CLIENT_ERROR_COLOR = Color.blue;

  public static final Color REDIRECT_COLOR = Color.green;

  private static final String TEXT_HTML = "text/html"; // $NON-NLS-1$

  private static final String HTML_COMMAND = "html"; // $NON-NLS-1$

  private static final String JSON_COMMAND = "json"; // $NON-NLS-1$

  private static final String XML_COMMAND = "xml"; // $NON-NLS-1$

  private static final String TEXT_COMMAND = "text"; // $NON-NLS-1$

  private static final String STYLE_SERVER_ERROR = "ServerError"; // $NON-NLS-1$

  private static final String STYLE_CLIENT_ERROR = "ClientError"; // $NON-NLS-1$

  private static final String STYLE_REDIRECT = "Redirect"; // $NON-NLS-1$

  private boolean textMode = true;

  private static final String ESC_CHAR_REGEX = "\\\\[\"\\\\/bfnrt]|\\\\u[0-9A-Fa-f]{4}"; // $NON-NLS-1$

  private static final String NORMAL_CHARACTER_REGEX = "[^\"\\\\]"// $NON-NLS-1$

  private static final String STRING_REGEX = "\"(" + ESC_CHAR_REGEX + "|" + NORMAL_CHARACTER_REGEX + ")*\""; // $NON-NLS-1$

  // This 'other value' regex is deliberately weak, even accepting an empty string, to be useful when reporting malformed data.
  private static final String OTHER_VALUE_REGEX = "[^\\{\\[\\]\\}\\,]*"; // $NON-NLS-1$

  private static final String VALUE_OR_PAIR_REGEX = "((" + STRING_REGEX + "\\s*:)?\\s*(" + STRING_REGEX + "|" + OTHER_VALUE_REGEX + ")\\s*,?\\s*)"; // $NON-NLS-1$

  private static final Pattern VALUE_OR_PAIR_PATTERN = Pattern.compile(VALUE_OR_PAIR_REGEX);

  // set default command to Text
  private String command = TEXT_COMMAND;

  // Keep copies of the two editors needed
  private static final EditorKit customisedEditor = new LocalHTMLEditorKit();

  private static final EditorKit defaultHtmlEditor = JEditorPane.createEditorKitForContentType(TEXT_HTML);

  private DefaultMutableTreeNode root;

  private DefaultTreeModel treeModel;

  private JTextPane stats;

  private JEditorPane results;

  private JScrollPane resultsScrollPane;

  private JPanel resultsPane;

  private JLabel imageLabel;

  private JTextArea sampleDataField;
 
  private JPanel requestPane;

  private JRadioButton textButton;

  private JRadioButton htmlButton;

  private JRadioButton jsonButton;

  private JRadioButton xmlButton;

  private JCheckBox downloadAll;

  private JTree jTree;

  private JTabbedPane rightSide;
 
  private static final ImageIcon imageSuccess = JMeterUtils.getImage(
          JMeterUtils.getPropDefault("viewResultsTree.success"//$NON-NLS-1$
              "icon_success_sml.gif")); //$NON-NLS-1$

  private static final ImageIcon imageFailure = JMeterUtils.getImage(
      JMeterUtils.getPropDefault("viewResultsTree.failure"//$NON-NLS-1$
          "icon_warning_sml.gif")); //$NON-NLS-1$
 
  public ViewResultsFullVisualizer() {
    super();
    log.debug("Start : ViewResultsFullVisualizer1");
    init();
    log.debug("End : ViewResultsFullVisualizer1");
  }

  public void add(SampleResult res) {
    updateGui(res);
  }

  public String getLabelResource() {
    return "view_results_tree_title"; // $NON-NLS-1$
  }

  /**
   * Update the visualizer with new data.
   */
  private synchronized void updateGui(SampleResult res) {
    log.debug("Start : updateGui1");
    if (log.isDebugEnabled()) {
      log.debug("updateGui1 : sample result - " + res);
    }
    // Add sample
    DefaultMutableTreeNode currNode = new DefaultMutableTreeNode(res);
    treeModel.insertNodeInto(currNode, root, root.getChildCount());
    addSubResults(currNode, res);
    // Add any assertion that failed as children of the sample node
    AssertionResult assertionResults[] = res.getAssertionResults();
    int assertionIndex = currNode.getChildCount();
    for (int j = 0; j < assertionResults.length; j++) {
      AssertionResult item = assertionResults[j];
     
      if (item.isFailure() || item.isError()) {
        DefaultMutableTreeNode assertionNode = new DefaultMutableTreeNode(item);
        treeModel.insertNodeInto(assertionNode, currNode, assertionIndex++);
      }
    }     

    if (root.getChildCount() == 1) {
      jTree.expandPath(new TreePath(root));
    }
    log.debug("End : updateGui1");
  }

  private void addSubResults(DefaultMutableTreeNode currNode, SampleResult res) {
    SampleResult[] subResults = res.getSubResults();

    int leafIndex = 0;

    for (int i = 0; i < subResults.length; i++) {
      SampleResult child = subResults[i];

      if (log.isDebugEnabled()) {
        log.debug("updateGui1 : child sample result - " + child);
      }
      DefaultMutableTreeNode leafNode = new DefaultMutableTreeNode(child);

      treeModel.insertNodeInto(leafNode, currNode, leafIndex++);
      addSubResults(leafNode, child);
            // Add any assertion that failed as children of the sample node
            AssertionResult assertionResults[] = child.getAssertionResults();
            int assertionIndex = leafNode.getChildCount();
            for (int j = 0; j < assertionResults.length; j++) {
                AssertionResult item = assertionResults[j];
               
                if (item.isFailure() || item.isError()) {
                    DefaultMutableTreeNode assertionNode = new DefaultMutableTreeNode(item);
                    treeModel.insertNodeInto(assertionNode, leafNode, assertionIndex++);
                }
            }
    }
  }

  /**
   * Clears the visualizer.
   */
  public void clearData() {
    log.debug("Start : clear1");

    if (log.isDebugEnabled()) {
      log.debug("clear1 : total child - " + root.getChildCount());
    }
    while (root.getChildCount() > 0) {
      // the child to be removed will always be 0 'cos as the nodes are
      // removed the nth node will become (n-1)th
      treeModel.removeNodeFromParent((DefaultMutableTreeNode) root.getChildAt(0));
    }

    results.setText("");// Response Data // $NON-NLS-1$
    sampleDataField.setText("");// Request Data // $NON-NLS-1$
    log.debug("End : clear1");
  }

  /**
   * Returns the description of this visualizer.
   *
   * @return description of this visualizer
   */
  public String toString() {
    String desc = "Shows the text results of sampling in tree form";

    if (log.isDebugEnabled()) {
      log.debug("toString1 : Returning description - " + desc);
    }
    return desc;
  }

  /**
   * Sets the right pane to correspond to the selected node of the left tree.
   */
  public void valueChanged(TreeSelectionEvent e) {
    log.debug("Start : valueChanged1");
    DefaultMutableTreeNode node = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();

    if (log.isDebugEnabled()) {
      log.debug("valueChanged : selected node - " + node);
    }

    StyledDocument statsDoc = stats.getStyledDocument();
    try {
      statsDoc.remove(0, statsDoc.getLength());
      sampleDataField.setText(""); // $NON-NLS-1$
      results.setText(""); // $NON-NLS-1$
      if (node != null) {
        Object userObject = node.getUserObject();
        if(userObject instanceof SampleResult) {         
          SampleResult res = (SampleResult) userObject;
         
          // We are displaying a SampleResult
          setupTabPaneForSampleResult();

          if (log.isDebugEnabled()) {
            log.debug("valueChanged1 : sample result - " + res);
          }

          // load time label

          log.debug("valueChanged1 : load time - " + res.getTime());
          String sd = res.getSamplerData();
          if (sd != null) {
            String rh = res.getRequestHeaders();
            if (rh != null) {
              StringBuffer sb = new StringBuffer(sd.length() + rh.length()+20);
              sb.append(sd);
              sb.append("\nRequest Headers:\n");
              sb.append(rh);
              sd = sb.toString();
            }
            sampleDataField.setText(sd);
          }

          StringBuffer statsBuff = new StringBuffer(200);
          statsBuff.append("Thread Name: ").append(res.getThreadName()).append(NL);
          String startTime = dateFormat.format(new Date(res.getStartTime()));
          statsBuff.append("Sample Start: ").append(startTime).append(NL);
          statsBuff.append("Load time: ").append(res.getTime()).append(NL);
          statsBuff.append("Latency: ").append(res.getLatency()).append(NL);
          statsBuff.append("Size in bytes: ").append(res.getBytes()).append(NL);
          statsBuff.append("Sample Count: ").append(res.getSampleCount()).append(NL);
          statsBuff.append("Error Count: ").append(res.getErrorCount()).append(NL);
          statsDoc.insertString(statsDoc.getLength(), statsBuff.toString(), null);
          statsBuff = new StringBuffer(); //reset for reuse
         
          String responseCode = res.getResponseCode();
          log.debug("valueChanged1 : response code - " + responseCode);

          int responseLevel = 0;
          if (responseCode != null) {
            try {
              responseLevel = Integer.parseInt(responseCode) / 100;
            } catch (NumberFormatException numberFormatException) {
              // no need to change the foreground color
            }
          }

          Style style = null;
          switch (responseLevel) {
          case 3:
            style = statsDoc.getStyle(STYLE_REDIRECT);
            break;
          case 4:
            style = statsDoc.getStyle(STYLE_CLIENT_ERROR);
            break;
          case 5:
            style = statsDoc.getStyle(STYLE_SERVER_ERROR);
            break;
          }

          statsBuff.append("Response code: ").append(responseCode).append(NL);
          statsDoc.insertString(statsDoc.getLength(), statsBuff.toString(), style);
          statsBuff = new StringBuffer(100); //reset for reuse

          // response message label
          String responseMsgStr = res.getResponseMessage();

          log.debug("valueChanged1 : response message - " + responseMsgStr);
          statsBuff.append("Response message: ").append(responseMsgStr).append(NL);

          statsBuff.append(NL).append("Response headers:").append(NL);
          statsBuff.append(res.getResponseHeaders()).append(NL);
          statsDoc.insertString(statsDoc.getLength(), statsBuff.toString(), null);
          statsBuff = null; // Done

          // get the text response and image icon
          // to determine which is NOT null
          if ((SampleResult.TEXT).equals(res.getDataType())) // equals(null) is OK
          {
            String response = getResponseAsString(res);
            if (command.equals(TEXT_COMMAND)) {
              showTextResponse(response);
            } else if (command.equals(HTML_COMMAND)) {
              showRenderedResponse(response, res);
            } else if (command.equals(JSON_COMMAND)) {
              showRenderJSONResponse(response);
            } else if (command.equals(XML_COMMAND)) {
              showRenderXMLResponse(res);
            }
          } else {
            byte[] responseBytes = res.getResponseData();
            if (responseBytes != null) {
              showImage(new ImageIcon(responseBytes)); //TODO implement other non-text types
            }
          }
        }
        else if(userObject instanceof AssertionResult) {
          AssertionResult res = (AssertionResult) userObject;
         
          // We are displaying an AssertionResult
          setupTabPaneForAssertionResult();
         
          if (log.isDebugEnabled()) {
            log.debug("valueChanged1 : sample result - " + res);
          }

          StringBuffer statsBuff = new StringBuffer(100);
          statsBuff.append("Assertion error: ").append(res.isError()).append(NL);
          statsBuff.append("Assertion failure: ").append(res.isFailure()).append(NL);
          statsBuff.append("Assertion failure message : ").append(res.getFailureMessage()).append(NL);
          statsDoc.insertString(statsDoc.getLength(), statsBuff.toString(), null);
          statsBuff = null;
        }
      }
    } catch (BadLocationException exc) {
      log.error("Error setting statistics text", exc);
      stats.setText("");
    }
    log.debug("End : valueChanged1");
  }

  private void showImage(Icon image) {
    imageLabel.setIcon(image);
    resultsScrollPane.setViewportView(imageLabel);
    textButton.setEnabled(false);
    htmlButton.setEnabled(false);
    jsonButton.setEnabled(false);
    xmlButton.setEnabled(false);
  }

  protected void showTextResponse(String response) {
    results.setContentType("text/plain"); // $NON-NLS-1$
    results.setText(response == null ? "" : response); // $NON-NLS-1$
    results.setCaretPosition(0);
    resultsScrollPane.setViewportView(results);

    textButton.setEnabled(true);
    htmlButton.setEnabled(true);
    jsonButton.setEnabled(true);
    xmlButton.setEnabled(true);
  }

  // It might be useful also to make this available in the 'Request' tab, for
  // when posting JSON.
  private static String prettyJSON(String json) {
    StringBuffer pretty = new StringBuffer(json.length() * 2); // Educated guess

    final String tab = ":   "; // $NON-NLS-1$
    StringBuffer index = new StringBuffer();
    String nl = ""; // $NON-NLS-1$

    Matcher valueOrPair = VALUE_OR_PAIR_PATTERN.matcher(json);

    boolean misparse = false;

    for (int i = 0; i < json.length(); ) {
      final char currentChar = json.charAt(i);
      if ((currentChar == '{') || (currentChar == '[')) {
        pretty.append(nl).append(index).append(currentChar);
        i++;
        index.append(tab);
        misparse = false;
      }
      else if ((currentChar == '}') || (currentChar == ']')) {
        if (index.length() > 0) {
          index.delete(0, tab.length());
        }
        pretty.append(nl).append(index).append(currentChar);
        i++;
        int j = i;
        while ((j < json.length()) && Character.isWhitespace(json.charAt(j))) {
          j++;
        }
        if ((j < json.length()) && (json.charAt(j) == ',')) {
          pretty.append(","); // $NON-NLS-1$
          i=j+1;
        }
        misparse = false;
      }
      else if (valueOrPair.find(i) && valueOrPair.group().length() > 0) {
        pretty.append(nl).append(index).append(valueOrPair.group());
        i=valueOrPair.end();
        misparse = false;
      }
      else {
        if (!misparse) {
          pretty.append(nl).append("- Parse failed from:");
        }
        pretty.append(currentChar);
        i++;
        misparse = true;
      }
      nl = "\n"; // $NON-NLS-1$
    }
    return pretty.toString();
  }
 
  private void showRenderJSONResponse(String response) {
    results.setContentType("text/plain"); // $NON-NLS-1$
    results.setText(response == null ? "" : prettyJSON(response));
    results.setCaretPosition(0);
    resultsScrollPane.setViewportView(results);

    textButton.setEnabled(true);
    htmlButton.setEnabled(true);
    jsonButton.setEnabled(true);
    xmlButton.setEnabled(true);
  }

  private void showRenderXMLResponse(SampleResult res) {
    results.setContentType("text/xml"); // $NON-NLS-1$
    results.setCaretPosition(0);
    byte[] source = res.getResponseData();
        final ByteArrayInputStream baIS = new ByteArrayInputStream(source);
      for(int i=0; i<source.length-XML_PFX.length; i++){
          if (JOrphanUtils.startsWith(source, XML_PFX, i)){
              baIS.skip(i);// Skip the leading bytes (if any)
              break;
          }
      }
    Component view = results;

    // there is also a javax.swing.text.Document class.
    org.w3c.dom.Document document = null;

        StringWriter sw = new StringWriter();
        Tidy tidy = XPathUtil.makeTidyParser(true, true, true, sw);
        document = tidy.parseDOM(baIS, null);
        document.normalize();
        if (tidy.getParseErrors() > 0) {
            showErrorMessageDialog(sw.toString(),
                    "Tidy: " + tidy.getParseErrors() + " errors, " + tidy.getParseWarnings() + " warnings",
                    JOptionPane.WARNING_MESSAGE);
        }

        JPanel domTreePanel = new DOMTreePanel(document);   
        view = domTreePanel;
    resultsScrollPane.setViewportView(view);
    textButton.setEnabled(true);
    htmlButton.setEnabled(true);
    jsonButton.setEnabled(true);
    xmlButton.setEnabled(true);
  }

  private static String getResponseAsString(SampleResult res) {

    String response = null;
    if ((SampleResult.TEXT).equals(res.getDataType())) {
      // Showing large strings can be VERY costly, so we will avoid
      // doing so if the response
      // data is larger than 200K. TODO: instead, we could delay doing
      // the result.setText
      // call until the user chooses the "Response data" tab. Plus we
      // could warn the user
      // if this happens and revert the choice if he doesn't confirm
      // he's ready to wait.
      int len = res.getResponseData().length;
      if (MAX_DISPLAY_SIZE > 0 && len > MAX_DISPLAY_SIZE) {
        response = "Response too large to be displayed. Size: " + len + " > Max: "+MAX_DISPLAY_SIZE;
        log.warn(response);
      } else {
        response = res.getResponseDataAsString();
      }
    }
    return response;
  }

  /**
   * Display the response as text or as rendered HTML. Change the text on the
   * button appropriate to the current display.
   *
   * @param e
   *            the ActionEvent being processed
   */
  public void actionPerformed(ActionEvent e) {
    command = e.getActionCommand();

    if (command != null
        && (command.equals(TEXT_COMMAND) || command.equals(HTML_COMMAND)
         || command.equals(JSON_COMMAND) || command.equals(XML_COMMAND))) {

      textMode = command.equals(TEXT_COMMAND);

      DefaultMutableTreeNode node = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();

      if (node == null) {
        results.setText("");
        return;
      }

      SampleResult res = (SampleResult) node.getUserObject();
      String response = getResponseAsString(res);

      if (command.equals(TEXT_COMMAND)) {
        showTextResponse(response);
      } else if (command.equals(HTML_COMMAND)) {
        showRenderedResponse(response, res);
      } else if (command.equals(JSON_COMMAND)) {
        showRenderJSONResponse(response);
      } else if (command.equals(XML_COMMAND)) {
        showRenderXMLResponse(res);
      }
    }
  }

  protected void showRenderedResponse(String response, SampleResult res) {
    if (response == null) {
      results.setText("");
      return;
    }

    int htmlIndex = response.indexOf("<HTML"); // could be <HTML lang=""> // $NON-NLS-1$

    // Look for a case variation
    if (htmlIndex < 0) {
      htmlIndex = response.indexOf("<html"); // ditto // $NON-NLS-1$
    }

    // If we still can't find it, just try using all of the text
    if (htmlIndex < 0) {
      htmlIndex = 0;
    }

    String html = response.substring(htmlIndex);

    /*
     * To disable downloading and rendering of images and frames, enable the
     * editor-kit. The Stream property can then be
     */

    // Must be done before setContentType
    results.setEditorKitForContentType(TEXT_HTML, downloadAll.isSelected() ? defaultHtmlEditor : customisedEditor);

    results.setContentType(TEXT_HTML);

    if (downloadAll.isSelected()) {
      // Allow JMeter to render frames (and relative images)
      // Must be done after setContentType [Why?]
      results.getDocument().putProperty(Document.StreamDescriptionProperty, res.getURL());
    }

    /*
     * Get round problems parsing <META http-equiv='content-type'
     * content='text/html; charset=utf-8'> See
     * http://issues.apache.org/bugzilla/show_bug.cgi?id=23315
     *
     * Is this due to a bug in Java?
     */
    results.getDocument().putProperty("IgnoreCharsetDirective", Boolean.TRUE); // $NON-NLS-1$

    results.setText(html);
    results.setCaretPosition(0);
    resultsScrollPane.setViewportView(results);

    textButton.setEnabled(true);
    htmlButton.setEnabled(true);
    jsonButton.setEnabled(true);
    xmlButton.setEnabled(true);
  }

  private Component createHtmlOrTextPane() {
    ButtonGroup group = new ButtonGroup();

    textButton = new JRadioButton(JMeterUtils.getResString("view_results_render_text")); // $NON-NLS-1$
    textButton.setActionCommand(TEXT_COMMAND);
    textButton.addActionListener(this);
    textButton.setSelected(textMode);
    group.add(textButton);

    htmlButton = new JRadioButton(JMeterUtils.getResString("view_results_render_html")); // $NON-NLS-1$
    htmlButton.setActionCommand(HTML_COMMAND);
    htmlButton.addActionListener(this);
    htmlButton.setSelected(!textMode);
    group.add(htmlButton);

    jsonButton = new JRadioButton(JMeterUtils.getResString("view_results_render_json")); // $NON-NLS-1$
    jsonButton.setActionCommand(JSON_COMMAND);
    jsonButton.addActionListener(this);
    jsonButton.setSelected(!textMode);
    group.add(jsonButton);

    xmlButton = new JRadioButton(JMeterUtils.getResString("view_results_render_xml")); // $NON-NLS-1$
    xmlButton.setActionCommand(XML_COMMAND);
    xmlButton.addActionListener(this);
    xmlButton.setSelected(!textMode);
    group.add(xmlButton);

    downloadAll = new JCheckBox(JMeterUtils.getResString("view_results_render_embedded")); // $NON-NLS-1$

    JPanel pane = new JPanel();
    pane.add(textButton);
    pane.add(htmlButton);
    pane.add(xmlButton);
    pane.add(jsonButton);
    pane.add(downloadAll);
    return pane;
  }

  /**
   * Initialize this visualizer
   */
  private void init() {
    setLayout(new BorderLayout(0, 5));
    setBorder(makeBorder());

    add(makeTitlePanel(), BorderLayout.NORTH);

    Component leftSide = createLeftPanel();
    rightSide = new JTabbedPane();
    // Add the common tab
    rightSide.addTab(JMeterUtils.getResString("view_results_tab_sampler"), createResponseMetadataPanel()); // $NON-NLS-1$
    // Create the panels for the other tabs
    requestPane = createRequestPanel();
    resultsPane = createResponseDataPanel();

    JSplitPane mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftSide, rightSide);
    add(mainSplit, BorderLayout.CENTER);
  }
 
  private void setupTabPaneForSampleResult() {
    // Set the title for the first tab
    rightSide.setTitleAt(0, JMeterUtils.getResString("view_results_tab_sampler")); //$NON-NLS-1$
    // Add the other tabs if not present
    if(rightSide.indexOfTab(JMeterUtils.getResString("view_results_tab_request")) < 0) { // $NON-NLS-1$
      rightSide.addTab(JMeterUtils.getResString("view_results_tab_request"), requestPane); // $NON-NLS-1$
    }
    if(rightSide.indexOfTab(JMeterUtils.getResString("view_results_tab_response")) < 0) { // $NON-NLS-1$
      rightSide.addTab(JMeterUtils.getResString("view_results_tab_response"), resultsPane); // $NON-NLS-1$
    }
  }
 
  private void setupTabPaneForAssertionResult() {
    // Set the title for the first tab
    rightSide.setTitleAt(0, JMeterUtils.getResString("view_results_tab_assertion")); //$NON-NLS-1$
    // Remove the other tabs if present
    int requestTabIndex = rightSide.indexOfTab(JMeterUtils.getResString("view_results_tab_request")); // $NON-NLS-1$
    if(requestTabIndex >= 0) {
      rightSide.removeTabAt(requestTabIndex);
    }
    int responseTabIndex = rightSide.indexOfTab(JMeterUtils.getResString("view_results_tab_response")); // $NON-NLS-1$
    if(responseTabIndex >= 0) {
      rightSide.removeTabAt(responseTabIndex);
    }
  }

  private Component createLeftPanel() {
    SampleResult rootSampleResult = new SampleResult();
    rootSampleResult.setSampleLabel("Root");
    rootSampleResult.setSuccessful(true);
    root = new DefaultMutableTreeNode(rootSampleResult);

    treeModel = new DefaultTreeModel(root);
    jTree = new JTree(treeModel);
    jTree.setCellRenderer(new ResultsNodeRenderer());
    jTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    jTree.addTreeSelectionListener(this);
    jTree.setRootVisible(false);
    jTree.setShowsRootHandles(true);

    JScrollPane treePane = new JScrollPane(jTree);
    treePane.setPreferredSize(new Dimension(200, 300));
    return treePane;
  }

  private Component createResponseMetadataPanel() {
    stats = new JTextPane();
    stats.setEditable(false);
    stats.setBackground(getBackground());

    // Add styles to use for different types of status messages
    StyledDocument doc = (StyledDocument) stats.getDocument();

    Style style = doc.addStyle(STYLE_REDIRECT, null);
    StyleConstants.setForeground(style, REDIRECT_COLOR);

    style = doc.addStyle(STYLE_CLIENT_ERROR, null);
    StyleConstants.setForeground(style, CLIENT_ERROR_COLOR);

    style = doc.addStyle(STYLE_SERVER_ERROR, null);
    StyleConstants.setForeground(style, SERVER_ERROR_COLOR);

    JScrollPane pane = makeScrollPane(stats);
    pane.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    return pane;
  }

  private JPanel createRequestPanel() {
    sampleDataField = new JTextArea();
    sampleDataField.setEditable(false);
    sampleDataField.setLineWrap(true);
    sampleDataField.setWrapStyleWord(true);

    JPanel pane = new JPanel(new BorderLayout(0, 5));
    pane.add(makeScrollPane(sampleDataField));
    return pane;
  }

  private JPanel createResponseDataPanel() {
    results = new JEditorPane();
    results.setEditable(false);

    resultsScrollPane = makeScrollPane(results);
    imageLabel = new JLabel();

    JPanel panel = new JPanel(new BorderLayout());
    panel.add(resultsScrollPane, BorderLayout.CENTER);
    panel.add(createHtmlOrTextPane(), BorderLayout.SOUTH);

    return panel;
  }

  private static class ResultsNodeRenderer extends DefaultTreeCellRenderer {
    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
        boolean leaf, int row, boolean focus) {
      super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, focus);
      boolean failure = true;
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if(userObject instanceof SampleResult) {
        failure = !(((SampleResult) userObject).isSuccessful());
      }
      else if(userObject instanceof AssertionResult) {
        AssertionResult assertion = (AssertionResult) userObject;
        failure =  assertion.isError() || assertion.isFailure();
      }
     
      // Set the status for the node
      if (failure) {
        this.setForeground(Color.red);
        this.setIcon(imageFailure);
      } else {
        this.setIcon(imageSuccess);
      }
      return this;
    }
  }

  private static class LocalHTMLEditorKit extends HTMLEditorKit {

    private static final ViewFactory defaultFactory = new LocalHTMLFactory();

    public ViewFactory getViewFactory() {
      return defaultFactory;
    }

    private static class LocalHTMLFactory extends javax.swing.text.html.HTMLEditorKit.HTMLFactory {
      /*
       * Provide dummy implementations to suppress download and display of
       * related resources: - FRAMEs - IMAGEs TODO create better dummy
       * displays TODO suppress LINK somehow
       */
      public View create(Element elem) {
        Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
        if (o instanceof HTML.Tag) {
          HTML.Tag kind = (HTML.Tag) o;
          if (kind == HTML.Tag.FRAME) {
            return new ComponentView(elem);
          } else if (kind == HTML.Tag.IMG) {
            return new ComponentView(elem);
          }
        }
        return super.create(elem);
      }
    }
  }

  /**
   *
   * A Dom tree panel for to display response as tree view author <a
   * href="mailto:d.maung@mdl.com">Dave Maung</a> TODO implement to find any
   * nodes in the tree using TreePath.
   *
   */
  private static class DOMTreePanel extends JPanel {

    private JTree domJTree;

    public DOMTreePanel(org.w3c.dom.Document document) {
      super(new GridLayout(1, 0));
      try {
        Node firstElement = getFirstElement(document);
        DefaultMutableTreeNode top = new XMLDefaultMutableTreeNode(firstElement);
        domJTree = new JTree(top);

        domJTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        domJTree.setShowsRootHandles(true);
        JScrollPane domJScrollPane = new JScrollPane(domJTree);
        domJTree.setAutoscrolls(true);
        this.add(domJScrollPane);
        ToolTipManager.sharedInstance().registerComponent(domJTree);
        domJTree.setCellRenderer(new DomTreeRenderer());
        this.setPreferredSize(new Dimension(800, 600));
      } catch (SAXException e) {
        log.warn("", e);
      }

    }

    /**
     * Skip all DTD nodes, all prolog nodes. They dont support in tree view
     * We let user to insert them however in DOMTreeView, we dont display it
     *
     * @param root
     * @return
     */
    private Node getFirstElement(Node parent) {
      NodeList childNodes = parent.getChildNodes();
      Node toReturn = parent; // Must return a valid node, or may generate an NPE
      for (int i = 0; i < childNodes.getLength(); i++) {
        Node childNode = childNodes.item(i);
        toReturn = childNode;
        if (childNode.getNodeType() == Node.ELEMENT_NODE){
            break;
        }

      }
      return toReturn;
    }

    /**
     * This class is to view as tooltext. This is very useful, when the
     * contents has long string and does not fit in the view. it will also
     * automatically wrap line for each 100 characters since tool tip
     * support html. author <a href="mailto:d.maung@mdl.com">Dave Maung</a>
     */
    private static class DomTreeRenderer extends DefaultTreeCellRenderer {
      public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
          boolean leaf, int row, boolean phasFocus) {
        super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, phasFocus);

        DefaultMutableTreeNode valueTreeNode = (DefaultMutableTreeNode) value;
        setToolTipText(getHTML(valueTreeNode.toString(), "<br>", 100)); // $NON-NLS-1$
        return this;
      }

      /**
       * get the html
       *
       * @param str
       * @param separator
       * @param maxChar
       * @return
       */
      private String getHTML(String str, String separator, int maxChar) {
        StringBuffer strBuf = new StringBuffer("<html><body bgcolor=\"yellow\"><b>"); // $NON-NLS-1$
        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++) {

          if (i % maxChar == 0 && i != 0) {
              strBuf.append(separator);
          }
          strBuf.append(encode(chars[i]));

        }
        strBuf.append("</b></body></html>"); // $NON-NLS-1$
        return strBuf.toString();

      }

      private String encode(char c) {
        String toReturn = String.valueOf(c);
        switch (c) {
        case '<': // $NON-NLS-1$
          toReturn = "&lt;"; // $NON-NLS-1$
          break;
        case '>': // $NON-NLS-1$
          toReturn = "&gt;"; // $NON-NLS-1$
          break;
        case '\'': // $NON-NLS-1$
          toReturn = "&apos;"; // $NON-NLS-1$
          break;
        case '\"': // $NON-NLS-1$
          toReturn = "&quot;"; // $NON-NLS-1$
          break;

        }
        return toReturn;
      }
    }
  }

    private static void showErrorMessageDialog(String message, String title, int messageType) {
        JOptionPane.showMessageDialog(null, message, title, messageType);
    }

}
TOP

Related Classes of org.apache.jmeter.visualizers.ViewResultsFullVisualizer$LocalHTMLEditorKit$LocalHTMLFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.