/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright the ZAP Dev team
*
* Licensed 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.zaproxy.zap.view;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Random;
import java.util.ResourceBundle;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.border.Border;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.WriterAppender;
import org.apache.log4j.spi.LoggingEvent;
import org.parosproxy.paros.Constant;
import org.zaproxy.zap.utils.DisplayUtils;
import org.zaproxy.zap.utils.FontUtils;
import org.zaproxy.zap.utils.ZapTextArea;
public class SplashScreen extends JFrame {
private static final String TIPS_PREFIX = "tips";
private static final String TIPS_TIP_PREFIX = TIPS_PREFIX + ".tip.";
private static final Logger LOGGER = Logger.getLogger(SplashScreen.class);
private static final long serialVersionUID = 1L;
private final static char NEWLINE = '\n';
private JScrollPane logScrollPane = null;
private JScrollPane tipsScrollPane = null;
private JProgressBar loadProgressBar = null;
private ZapTextArea logPanel = null;
private ZapTextArea tipsPanel = null;
// Tips and Tricks related variables
private List<String> tipsAndTricks = null;
private final Random random = new Random();
private boolean tipsLoaded = false;
private double currentPerc;
private SplashOutputWriter splashOutputWriter;
public SplashScreen() {
super();
setSize(DisplayUtils.getScaledDimension(420, 420));
setLocationRelativeTo(null);
setTitle(Constant.PROGRAM_NAME);
setIconImages(DisplayUtils.getZapIconImages());
BackgroundImagePanel panel = new BackgroundImagePanel();
panel.setPreferredSize(DisplayUtils.getScaledDimension(420, 420)); //420x560
panel.setLayout(new GridBagLayout());
panel.setBackgroundImage(SplashScreen.class.getResource("/resource/zap-splash-background.png"));
Border margin = BorderFactory.createEtchedBorder(javax.swing.border.EtchedBorder.RAISED);
Border padding = BorderFactory.createEmptyBorder(4, 4, 4, 4);
panel.setBorder(BorderFactory.createCompoundBorder(margin, padding));
JLabel lblVersion = new JLabel();
JLabel lblProgramName = new JLabel();
lblProgramName.setText(Constant.PROGRAM_NAME);
lblProgramName.setFont(FontUtils.getFont(Font.BOLD, FontUtils.Size.huge));
lblProgramName.setVisible(true);
lblProgramName.setName("lblProgramName");
lblVersion.setText(Constant.PROGRAM_VERSION);
lblVersion.setFont(FontUtils.getFont(FontUtils.Size.much_larger));
lblVersion.setName("lblVersion");
// ProgramName is at the beginning of the panel (0,0)
panel.add(lblProgramName, LayoutHelper.getGBC(0, 0, 2, 1));
// Version is +8 horizontally respect to the other components
panel.add(lblVersion, LayoutHelper.getGBC(0, 1, 2, 1, new Insets(0, 8, 0, 8)));
// Progress bar (height 12) is +56 and then +24
// vertically respect the other elements (tot + 92)
panel.add(getLoadingJProgressBar(), LayoutHelper.getGBC(0, 2, 1, 1.0, new Insets(56, 0, 24, 0)));
panel.add(Box.createHorizontalGlue(), LayoutHelper.getGBC(1, 2, 1, 1.0));
// Panels should be with different heights for a good view
panel.add(getTipsJScrollPane(), LayoutHelper.getGBC(0, 3, 2, 1.0, 1.0));
panel.add(getLogJScrollPane(), LayoutHelper.getGBC(0, 4, 2, 1.0, 0.5));
this.add(panel);
this.pack();
splashOutputWriter = new SplashOutputWriter();
Logger.getRootLogger().addAppender(splashOutputWriter);
setVisible(true);
}
/**
* Set the completion percentage value
* @param percentage the percentage of completion
*/
public void setLoadingCompletion(double percentage) {
currentPerc = percentage;
updateLoadingJProgressBar();
}
public void addLoadingCompletion(double value) {
currentPerc += value;
updateLoadingJProgressBar();
}
private void updateLoadingJProgressBar() {
if (!EventQueue.isDispatchThread()) {
try {
EventQueue.invokeAndWait(new Runnable() {
@Override
public void run() {
updateLoadingJProgressBar();
}
});
} catch (InvocationTargetException e) {
LOGGER.error("Failed to update progress bar: ", e);
} catch (InterruptedException ignore) {
}
return;
}
if (currentPerc > 100) {
currentPerc = 100;
} else if (currentPerc < 0) {
currentPerc = 0;
}
getLoadingJProgressBar().setValue((int)currentPerc);
}
private JProgressBar getLoadingJProgressBar() {
if (loadProgressBar == null) {
loadProgressBar = new JProgressBar();
loadProgressBar.setPreferredSize(DisplayUtils.getScaledDimension(100, 12));
loadProgressBar.setMinimum(0);
loadProgressBar.setMaximum(100);
loadProgressBar.setValue(50);
setLoadingCompletion(0.0D);
}
return loadProgressBar;
}
private JScrollPane getLogJScrollPane() {
if (logScrollPane == null) {
logScrollPane = new JScrollPane();
logScrollPane.setViewportView(getLogPanel());
}
return logScrollPane;
}
private JScrollPane getTipsJScrollPane() {
if (tipsScrollPane == null) {
tipsScrollPane = new JScrollPane();
tipsScrollPane.setViewportView(getTipsPanel());
}
return tipsScrollPane;
}
private ZapTextArea getLogPanel() {
if (logPanel == null) {
logPanel = new ZapTextArea();
logPanel.setEditable(false);
logPanel.setLineWrap(true);
logPanel.setName("");
logPanel.append(Constant.messages.getString("start.splash.start"));
}
return logPanel;
}
private ZapTextArea getTipsPanel() {
if (tipsPanel == null) {
tipsPanel = new ZapTextArea();
tipsPanel.setEditable(false);
tipsPanel.setLineWrap(true);
tipsPanel.setWrapStyleWord(true);
tipsPanel.setName("");
tipsPanel.append(Constant.messages.getString("start.splash.tips.loading"));
displayRandomTip();
}
return tipsPanel;
}
private void displayRandomTip() {
if (tipsLoaded || this.getTipsAndTricks() == null) {
// Already shown or not loaded yet
return;
}
if (this.getTipsAndTricks().isEmpty()) {
// No tips :(
this.getTipsPanel().setText(Constant.messages.getString("start.splash.tips.none"));
this.tipsLoaded = true;
return;
}
this.getTipsPanel().setText(Constant.messages.getString("start.splash.tips.title"));
this.getTipsPanel().append(this.getRandomTip());
this.tipsLoaded = true;
}
/**
* Close current splash screen
*/
public void close() {
Logger.getRootLogger().removeAppender(splashOutputWriter);
dispose();
}
/**
* Append a message to the output window of this splash screen
* @param msg the message that should be appended
*/
public void appendMsg(final String msg) {
if (!EventQueue.isDispatchThread()) {
try {
EventQueue.invokeAndWait(new Runnable() {
@Override
public void run() {
appendMsg(msg);
}
});
} catch (InvocationTargetException e) {
LOGGER.error("Failed to append message: ", e);
} catch (InterruptedException ignore) {
}
return;
}
displayRandomTip();
getLogPanel().append(msg);
JScrollBar vertical = getLogJScrollPane().getVerticalScrollBar();
vertical.setValue(vertical.getMaximum());
}
private class SplashOutputWriter extends WriterAppender {
@Override
public void append(final LoggingEvent event) {
if (!EventQueue.isDispatchThread()) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
append(event);
}
});
return;
}
if (event.getLevel().equals(Level.INFO)) {
String renderedmessage = event.getRenderedMessage();
if (renderedmessage != null) {
appendMsg(new StringBuilder("INFO: ").append(renderedmessage).append(NEWLINE).toString());
}
} else if (event.getLevel().equals(Level.ERROR)) {
String renderedmessage = event.getRenderedMessage();
if (renderedmessage != null) {
appendMsg(new StringBuilder("ERROR: ").append(renderedmessage).append(NEWLINE).toString());
}
}
}
}
private List<String> getTipsAndTricks() {
if (tipsAndTricks == null) {
// Need to load them in
ResourceBundle rb = Constant.messages.getMessageBundle(TIPS_PREFIX);
if (rb == null) {
return null;
}
tipsAndTricks = new ArrayList<>();
Enumeration<String> enm = rb.getKeys();
while (enm.hasMoreElements()) {
String key = enm.nextElement();
if (key.startsWith(TIPS_TIP_PREFIX)) {
tipsAndTricks.add(rb.getString(key));
}
}
}
return this.tipsAndTricks;
}
private String getRandomTip() {
return this.getTipsAndTricks().get(random.nextInt(this.getTipsAndTricks().size()));
}
}