/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/
package org.geomajas.gwt.client.widget;
import com.google.gwt.core.client.GWT;
import com.smartgwt.client.widgets.events.CloseClickEvent;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import org.geomajas.annotation.Api;
import org.geomajas.global.ExceptionDto;
import org.geomajas.gwt.client.i18n.GlobalMessages;
import org.geomajas.gwt.client.i18n.I18nProvider;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.widgets.Button;
import com.smartgwt.client.widgets.HTMLFlow;
import com.smartgwt.client.widgets.HTMLPane;
import com.smartgwt.client.widgets.Img;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import org.geomajas.gwt.client.util.HtmlBuilder;
import org.geomajas.gwt.client.util.WidgetLayout;
/**
* <p>
* Modal error messaging window that displays an exception. This window is used in case the server throws up an
* exception during command execution. This window shows the general exception message, and adds an option to view more
* details about the error. The details will then display the Java class name of the exception and the entire stack
* trace.
* </p>
*
* @author Pieter De Graef
* @author Joachim Van der Auwera
* @since 1.10.0
*/
@Api
public class ExceptionWindow extends Window implements CloseClickHandler {
private static final GlobalMessages MESSAGES = GWT.create(GlobalMessages.class);
/** The list of exceptions this window should display. */
private ExceptionDto error;
/** The button that displays the details about the exception upon clicking. */
private Button expandButton;
/** The actual layout that contains the details. Invisible by default. */
private VLayout detailsLayout;
// ------------------------------------------------------------------------
// Constructors:
// ------------------------------------------------------------------------
/**
* Create a new error messaging window displaying a single exception.
*
* @param error The exception to display.
* @since 1.10.0
*/
@Api
public ExceptionWindow(ExceptionDto error) {
super();
this.error = error;
buildGui();
setDetailsVisible(false);
}
@Override
public void onDraw() {
// try to force to be inside the screen
if (WidgetLayout.exceptionWindowKeepInScreen) {
WidgetLayout.keepWindowInScreen(this);
}
super.onDraw();
}
// ------------------------------------------------------------------------
// Private methods:
// ------------------------------------------------------------------------
/** Build the entire GUI for this widget. */
private void buildGui() {
setTitle(I18nProvider.getGlobal().commandError());
setHeaderIcon(WidgetLayout.iconError);
setIsModal(true);
setShowModalMask(true);
setModalMaskOpacity(WidgetLayout.modalMaskOpacity);
setWidth(WidgetLayout.exceptionWindowWidth);
setHeight(WidgetLayout.exceptionWindowHeightNormal);
setKeepInParentRect(WidgetLayout.exceptionWindowKeepInScreen);
setCanDragResize(true);
centerInPage();
setAutoSize(true);
addCloseClickHandler(this);
addItem(createErrorLayout(error));
}
/**
* Method which is called when the window is closed.
*
* @param event close event
*/
public void onCloseClick(CloseClickEvent event) {
destroy();
}
/**
* Create the GUI for a single exception.
*
* @param error error to report
* @return layout
*/
private VLayout createErrorLayout(ExceptionDto error) {
VLayout layout = new VLayout();
layout.setWidth100();
layout.setHeight100();
layout.setPadding(WidgetLayout.marginLarge);
HLayout topLayout = new HLayout(WidgetLayout.marginLarge);
topLayout.setWidth100();
Img icon = new Img(WidgetLayout.iconError,
WidgetLayout.exceptionWindowIconSize, WidgetLayout.exceptionWindowIconSize);
topLayout.addMember(icon);
HTMLFlow message = new HTMLFlow();
message.setWidth100();
message.setHeight100();
message.setLayoutAlign(VerticalAlignment.TOP);
message.setContents(HtmlBuilder.divStyle(WidgetLayout.exceptionWindowMessageStyle, error.getMessage()));
topLayout.addMember(message);
layout.addMember(topLayout);
if (error.getStackTrace() != null && error.getStackTrace().length > 0) {
expandButton = new Button(MESSAGES.exceptionDetailsView());
expandButton.setWidth(WidgetLayout.exceptionWindowButtonWidth);
expandButton.setLayoutAlign(Alignment.RIGHT);
expandButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
setDetailsVisible(!detailsLayout.isVisible());
}
});
layout.addMember(expandButton);
String content = getDetails(error);
HTMLPane detailPane = new HTMLPane();
detailPane.setContents(content);
detailPane.setWidth100();
detailPane.setHeight100();
detailsLayout = new VLayout();
detailsLayout.setWidth100();
detailsLayout.setHeight100();
detailsLayout.addMember(detailPane);
detailsLayout.setBorder(WidgetLayout.exceptionWindowDetailBorderStyle);
layout.addMember(detailsLayout);
}
return layout;
}
/**
* Build details message for an exception.
*
* @param error error to build message for
* @return HTML string with details message
*/
private String getDetails(ExceptionDto error) {
if (null == error) {
return "";
}
StringBuilder content = new StringBuilder();
String header = error.getClassName();
if (error.getExceptionCode() != 0) {
header += " (" + error.getExceptionCode() + ")";
}
content.append(HtmlBuilder.divStyle(WidgetLayout.exceptionWindowDetailHeaderStyle, header));
for (StackTraceElement el : error.getStackTrace()) {
String style = WidgetLayout.exceptionWindowDetailTraceNormalStyle;
String line = el.toString();
if ((line.startsWith("org.geomajas.") && !line.startsWith("org.geomajas.example.")) ||
line.startsWith("org.springframework.") ||
line.startsWith("org.hibernate.") ||
line.startsWith("org.hibernatespatial.") ||
line.startsWith("org.geotools.") ||
line.startsWith("com.vividsolutions.") ||
line.startsWith("org.mortbay.jetty.") ||
line.startsWith("sun.") ||
line.startsWith("java.") ||
line.startsWith("javax.") ||
line.startsWith("com.google.") ||
line.startsWith("$Proxy")) {
style = WidgetLayout.exceptionWindowDetailTraceLessStyle;
}
content.append(HtmlBuilder.divStyle(style, line));
}
content.append(getDetails(error.getCause()));
return content.toString();
}
/**
* Toggle the visibility of the exception details.
*
* @param detailsVisible should details be visible
*/
private void setDetailsVisible(boolean detailsVisible) {
detailsLayout.setVisible(detailsVisible);
if (detailsVisible) {
setAutoSize(false);
expandButton.setTitle(MESSAGES.exceptionDetailsHide());
setHeight(WidgetLayout.exceptionWindowHeightDetails);
} else {
expandButton.setTitle(MESSAGES.exceptionDetailsView());
setHeight(WidgetLayout.exceptionWindowHeightNormal);
}
}
}