blob: 6b353df4c7612e7f41386007d7d2f6801d5914fc [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*/
package org.eclipse.osbp.xtext.reportdsl.common;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.eclipse.birt.report.model.api.DesignElementHandle;
import org.eclipse.birt.report.model.api.OdaDataSetHandle;
import org.eclipse.birt.report.model.api.OdaDataSourceHandle;
import org.eclipse.birt.report.model.api.ReportDesignHandle;
import org.eclipse.birt.report.model.api.SessionHandle;
import org.eclipse.birt.report.model.api.activity.SemanticException;
import org.eclipse.birt.report.model.api.command.ContentException;
import org.eclipse.birt.report.model.api.command.NameException;
import org.eclipse.birt.report.model.api.css.CssStyleSheetHandle;
import org.eclipse.birt.report.model.css.CssStyleSheet;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement;
import org.eclipse.osbp.dsl.semantic.entity.LEntity;
import org.eclipse.osbp.osgi.hybrid.api.AbstractHybridVaaclipseView;
import org.eclipse.osbp.preferences.ProductConfiguration;
import org.eclipse.osbp.runtime.common.event.EventDispatcherEvent;
import org.eclipse.osbp.runtime.common.event.EventDispatcherEvent.EventDispatcherCommand;
import org.eclipse.osbp.runtime.common.event.EventDispatcherEvent.EventDispatcherDataTag;
import org.eclipse.osbp.runtime.common.event.IEventDispatcher;
import org.eclipse.osbp.ui.api.customfields.IBlobService;
import org.eclipse.osbp.ui.api.datamart.DatamartFilter;
import org.eclipse.osbp.ui.api.datamart.IDataMart;
import org.eclipse.osbp.ui.api.datamart.IDatamartFilterGenerator;
import org.eclipse.osbp.ui.api.layout.IViewLayoutManager;
import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService;
import org.eclipse.osbp.ui.api.perspective.IPerspectiveProvider;
import org.eclipse.osbp.ui.api.report.IReportProvider.Rendering;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService;
import org.eclipse.osbp.ui.api.user.IUser;
import org.eclipse.osbp.utils.common.EntityUtils;
import org.eclipse.osbp.utils.constants.ExtendedDate;
import org.eclipse.osbp.utils.vaadin.ViewLayoutManager;
import org.eclipse.osbp.xtext.action.ReportActionEnum;
import org.eclipse.osbp.xtext.action.common.IToolbarAction;
import org.eclipse.osbp.xtext.datamart.common.DatamartFilterGenerator;
import org.eclipse.osbp.xtext.datamartdsl.DatamartDSLPackage;
import org.eclipse.osbp.xtext.datamartdsl.DatamartDefinition;
import org.eclipse.osbp.xtext.datamartdsl.DatamartEntity;
import org.eclipse.osbp.xtext.datamartdsl.DatamartNavigation;
import org.eclipse.osbp.xtext.reportdsl.common.item.BodyItem;
import org.eclipse.osbp.xtext.reportdsl.jvmmodel.ReportGeneratorWorkSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ibm.icu.util.ULocale;
import com.vaadin.data.Container.Filter;
import com.vaadin.server.DownloadStream;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;
import com.vaadin.ui.BrowserFrame;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
/**
*
* @author mokross, riegel
*
*/
public abstract class BaseReport extends AbstractHybridVaaclipseView
implements StreamResource.StreamSource, IUser.UserLocaleListener, IDatamartFilterGenerator.FilterChangeListener,
IEventDispatcher.Receiver {
private static final String BODY_HTML = "</body></html>";
private static final String HTML_BODY = "<html><body>";
private static final String RPTDESIGN = ".rptdesign";
private static final long serialVersionUID = -2774341697829389431L;
private static final Logger LOGGER = LoggerFactory.getLogger(BaseReport.class);
public static final String CAPTION_I18NKEY_SAVE_PDF = "save PDF";
public static final String CAPTION_I18NKEY_PRINT_PDF = "print PDF";
protected transient IViewLayoutManager layoutManager;
protected transient BrowserFrame frame;
protected transient Map<String, IDatamartFilterGenerator> filterGeneratorMap = new HashMap<>();
protected transient BodyItem body;
protected transient ReportGeneratorWorkSet workset;
protected Locale locale;
protected StreamResource reportResource;
protected transient Map<String, IDataMart> datamartInstanceMap = new HashMap<>();
/** needs to be equivalent to the dataMart ODA bundle symbolic name */
public static final String DATASOURCE_EXTENSION_ID = "org.eclipse.osbp.xtext.reportdsl.oda.datamart";
/**
* in order to have data in header and footer areas we must provide them as
* translations. to get not mixed up with attribute name translations, this
* prefix must be used
*/
public static final String ATTRIBUTE_TRANSLATION_PREFIX = "$$$";
@Inject
private IEclipseContext eclipseContext;
@Inject
protected IThemeResourceService themeResourceService;
@Inject
private IUser user;
@Inject
private IDSLMetadataService dslMetadataService;
@Inject
private IBlobService blobService;
@Inject
private IEventDispatcher eventDispatcher;
private Object selectedId = null;
private List<Parameter> parameterList;
@Inject
public BaseReport() {
super();
layoutManager = new ViewLayoutManager();
}
@Inject
public BaseReport(VerticalLayout parent, IEclipseContext context, MApplication app) {
super(parent, context, app);
layoutManager = new ViewLayoutManager();
layoutManager.init(parent);
workset = new ReportGeneratorWorkSet();
}
@PostConstruct
public void activate() {
super.initView();
user.addUserLocaleListener(this);
this.locale = user.getLocale();
for (IDatamartFilterGenerator filterGenerator : filterGeneratorMap.values()) {
filterGenerator.addFilterChangeListener(this);
}
eventDispatcher.addEventReceiver(this);
}
@PreDestroy
public void deactivate() {
user.removeUserLocaleListener(this);
for (IDatamartFilterGenerator filterGenerator : filterGeneratorMap.values()) {
filterGenerator.removeFilterChangeListener(this);
}
eventDispatcher.removeEventReceiver(this);
super.destroyView();
}
public StreamResource getReportStreamResource() {
return getReportStreamResource(locale);
}
public StreamResource getReportStreamResource(Locale locale) {
setLocale(locale);
return reportResource;
}
@Override
protected void createView(VerticalLayout parent) {
initReport();
}
protected boolean getSelectById() {
return false;
}
protected Object getSelectedId() {
return selectedId;
}
public List<Parameter> getParameterList() {
if (parameterList == null){
parameterList = new ArrayList<>();
}
return parameterList;
}
public void setParameterList(List<Parameter> parameterList){
this.parameterList = parameterList;
}
protected abstract List<String> getStaticData();
public void addCss(ReportDesignHandle designHandle) {
if (getExternalCssURI() != null) {
CssStyleSheet css = new CssStyleSheet();
css.setFileName(getExternalCssURI());
css.setExternalCssURI(getExternalCssURI());
CssStyleSheetHandle cssh = new CssStyleSheetHandle(designHandle.getModuleHandle(), css);
try {
designHandle.addCss(cssh);
} catch (SemanticException e) {
LOGGER.error(e.getLocalizedMessage());
if (e.getCause() != null)
LOGGER.error(" " + e.getCause());
}
}
}
protected final void initReport() {
try {
LOGGER.debug("BaseReport " + getClass().getSimpleName() + " initReport");
SessionHandle session = BirtEngine.getDesignEngine().newSessionHandle(ULocale.forLocale(user.getLocale()));
SessionHandle.setBirtResourcePath("i18n/I18N");
if (getUsedReportDesignFilename() == null) {
// Create a new report design.
workset.setDesign(session.createDesign());
// The element factory creates instances of the various BIRT
// elements.
workset.setFactory(workset.getDesign().getElementFactory());
// Create a simple master page that describes how the report
// will appear when printed.
//
// Note: The report will fail to load in the BIRT designer
// unless you create a master page.
DesignElementHandle element = workset.getFactory().newSimpleMasterPage("Page Master"); //$NON-NLS-1$
workset.getDesign().getMasterPages().add(element);
// add external CSS
addCss(workset.getDesign());
body = new BodyItem(workset);
}
} catch (Exception e) {
LOGGER.error(e.getLocalizedMessage());
if (e.getCause() != null)
LOGGER.error(" " + e.getCause());
}
}
protected final void createFilter() {
datamartInstanceMap = getDatamartInstanceMap(user);
for (Entry<String, IDataMart> datamartInstanceEntry : datamartInstanceMap.entrySet()) {
if (filterGeneratorMap.get(datamartInstanceEntry.getKey()) == null) {
DatamartFilterGenerator filterGenerator = new DatamartFilterGenerator(datamartInstanceEntry.getValue(),
getContext(), isShowCaption(), Integer.max(10, getNumMultiRows()));
filterGenerator.createUIFilters(layoutManager);
filterGeneratorMap.put(datamartInstanceEntry.getKey(), filterGenerator);
}
}
}
public Map<String, IDataMart> getDatamartInstanceMap() {
return datamartInstanceMap;
}
protected abstract Map<String, IDataMart> getDatamartInstanceMap(IUser user);
protected abstract int getNumMultiRows();
protected abstract boolean isShowCaption();
protected abstract String getPersistenceUnitName();
protected abstract boolean isParametrized();
/**
* @return the explicit given file name of the report design file
*/
protected String getReportDesignFilename() {
return null;
}
/**
* @return either the explicit given file name, any automatic generated
* report design file or null
*/
protected final String getUsedReportDesignFilename() {
// --- either the explicit given file name ---
String retcode = getReportDesignFilename();
if (retcode == null) {
String pathName = getClass().getCanonicalName().replace(".", "/");
if (pathName.endsWith("Report")) {
pathName = pathName.substring(0, pathName.length() - "Report".length());
}
// --- or a copy of the automatic generated report file ---
retcode = "rptdesign/" + pathName + RPTDESIGN;
try {
getClass().getClassLoader().getResource(retcode).openStream();
} catch (Exception e1) {
// --- or the automatic generated report file ---
retcode = "rptdesign-gen/" + pathName + RPTDESIGN;
try {
getClass().getClassLoader().getResource(retcode).openStream();
} catch (Exception e2) {
retcode = null;
}
}
}
return retcode;
}
protected String getExternalCssURI() {
return null;
}
protected String getExternalCssURIBundle() {
return null;
}
protected Rendering getRenderingAs() {
return Rendering.PDF_STREAM;
}
protected String getReportFilename() {
return getClass().getSimpleName() + getTimestampPostfix() + "-" + user.getLocale().toString();
}
protected final String getFileExtension() {
return getFileExtension(getRenderingAs());
}
final String getFileExtension(Rendering renderer) {
switch (renderer) {
case HTML_FILE:
case HTML_STREAM:
return ".html";
case PDF_FILE:
case PDF_STREAM:
case PDF_PRINT_STREAM:
return ".pdf";
default:
return ".unknown";
}
}
protected final String getTimestampPostfix() {
return "-" + ExtendedDate.timeStampToString();
}
@Override
protected final void createComponents() {
if (renderAllowed()){
// remove any previous component
if (frame != null) {
layoutManager.getDataArea().removeComponent(frame);
frame = null;
}
frame = new BrowserFrame(null, null);
frame.setAlternateText("unable to display report");
frame.setSizeFull();
layoutManager.getDataArea().addComponent(frame);
layoutManager.getDataArea().setExpandRatio(frame, 1);
String filename = getReportFilename() + getFileExtension();
reportResource = new StreamResource(this, filename);
reportResource.setCacheTime(0);
frame.setSource(reportResource);
}
}
private boolean renderAllowed(){
if (getSelectById() && getSelectedId()==null) {
for (IDataMart datamartInstance : getDatamartInstanceMap().values()) {
for (DatamartFilter filter : datamartInstance.getFilters()) {
if(!filter.isOptional()) {
return true;
}
}
}
return false;
}
return true;
}
@SuppressWarnings("serial")
public void addPDFDownload(IToolbarAction target) {
MToolBar toolbar = target.getToolBar();
for (MToolBarElement element : toolbar.getChildren()) {
Object widget = element.getWidget();
if (widget instanceof Button) {
List<String> tags = element.getTags();
if (tags.contains(ReportActionEnum.REPORT_ACTION_DOWNLOAD_PDF.getLiteral())) {
FileDownloader fileDownloader = new FileDownloader(
new StreamResource(this, getReportFilename() + ".pdf") {
@Override
public DownloadStream getStream() {
final DownloadStream ds = new DownloadStream(getPDF(), getMIMEType(),
getFilename());
ds.setBufferSize(getBufferSize());
ds.setCacheTime(getCacheTime());
return ds;
}
});
fileDownloader.extend((Button) widget);
}
if (tags.contains(ReportActionEnum.REPORT_ACTION_PRINT_ON_SERVER_PDF.getLiteral())) {
((Button) widget).addClickListener(new Button.ClickListener() {
private static final long serialVersionUID = 3396112960709977314L;
@Override
public void buttonClick(ClickEvent event) {
String reportPrinterPref = ProductConfiguration.prefs().getReportPrinter(getReportName());
String printer = (reportPrinterPref.length()!=0)?reportPrinterPref:user.getPrintService();
PdfPrintService.printReportAsPdf(BaseReport.this, printer);
}
});
}
}
}
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public Locale getLocale() {
return locale;
}
public void buildReportDesign(String fullQualifiedFileName) throws IOException {
try {
URL baseUrl = getClass().getClassLoader().getResource("/rptdesign-gen/.");
if (LOGGER.isDebugEnabled())
LOGGER.debug(baseUrl.toString());
} catch (Exception e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionDetails = sw.toString();
LOGGER.error("{}", exceptionDetails);
}
workset.getDesign().saveAs(fullQualifiedFileName);
}
protected final Object getRunnableOrFilename() {
if (getUsedReportDesignFilename() != null) {
return getUsedReportDesignFilename();
} else {
return workset.getDesign();
}
}
protected void add(DesignElementHandle handle) {
if (handle instanceof OdaDataSourceHandle) {
workset.getDesign().getModule().addElementID(handle.getElement());
// design.
} else if (handle instanceof OdaDataSetHandle) {
workset.getDesign().getModule().addElementID(handle.getElement());
} else {
try {
workset.getDesign().getBody().add(handle);
} catch (ContentException | NameException e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionDetails = sw.toString();
LOGGER.error("exception occured:{}", exceptionDetails);
}
}
}
public final ByteArrayInputStream getPDF(String path) {
try {
return BirtEngine.get(this, Rendering.PDF_FILE, path);
} catch (Exception e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionDetails = sw.toString();
LOGGER.error("exception PDF occured:{}", exceptionDetails);
return null;
}
}
public final ByteArrayInputStream getHTML(String path) {
try {
return BirtEngine.get(this, Rendering.HTML_FILE, path);
} catch (Exception e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionDetails = sw.toString();
LOGGER.error("exception HTML occured:{}", exceptionDetails);
return new ByteArrayInputStream(
(HTML_BODY + getClass().getSimpleName() + ": " + e.getLocalizedMessage() + BODY_HTML).getBytes());
}
}
public ByteArrayInputStream getPDF() {
try {
return BirtEngine.get(this, Rendering.PDF_STREAM);
} catch (Exception e) {
LOGGER.debug(getClass().getSimpleName() + " PDF_STREAM: " + e.getLocalizedMessage() + BODY_HTML);
return new ByteArrayInputStream(
(HTML_BODY + getClass().getSimpleName() + ": " + e.getLocalizedMessage() + BODY_HTML).getBytes());
}
}
public ByteArrayInputStream getPDFPrint() {
try {
return BirtEngine.get(this, Rendering.PDF_PRINT_STREAM);
} catch (Exception e) {
LOGGER.debug(getClass().getSimpleName() + " PDF_PRINT_STREAM: " + e.getLocalizedMessage() + BODY_HTML);
return new ByteArrayInputStream(
(HTML_BODY + getClass().getSimpleName() + ": " + e.getLocalizedMessage() + BODY_HTML).getBytes());
}
}
public ByteArrayInputStream getHTML() {
try {
return BirtEngine.get(this, Rendering.HTML_STREAM);
} catch (Exception e) {
LOGGER.debug(getClass().getSimpleName() + " HTML_STREAM: " + e.getLocalizedMessage() + BODY_HTML);
return new ByteArrayInputStream(
(HTML_BODY + getClass().getSimpleName() + ": " + e.getLocalizedMessage() + BODY_HTML).getBytes());
}
}
public ByteArrayInputStream getStream() {
return getStream(getRenderingAs());
}
protected ByteArrayInputStream getStream(Rendering renderer) {
try {
switch (renderer) {
case HTML_STREAM:
return getHTML();
case PDF_STREAM:
return getPDF();
case PDF_PRINT_STREAM:
return getPDFPrint();
default:
return null;
}
} catch (Exception e) {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionDetails = sw.toString();
LOGGER.error("{}", exceptionDetails);
}
return null;
}
public IEclipseContext getEclipseContext() {
return eclipseContext;
}
public IDSLMetadataService getDslMetadataService() {
return dslMetadataService;
}
public IUser getUser() {
return user;
}
@Focus
public void setFocus() {
Component parent = getParent();
while (!(parent instanceof Panel) && parent != null) {
parent = parent.getParent();
}
if (parent != null) {
((Panel) parent).focus();
}
}
public void localeChanged(Locale locale) {
setLocale(locale);
if (layoutManager != null) {
layoutManager.setLabelValue(dslMetadataService.translate(locale.toLanguageTag(), getReportName()));
}
renderData();
}
@Override
public void filterChanged(DatamartFilter changedFilter) {
if (changedFilter != null) {
MPerspective perspective = getContext().get(MPerspective.class);
EventDispatcherEvent evnt = new EventDispatcherEvent(perspective, EventDispatcherCommand.SELECT, changedFilter.getName(), getClass().getCanonicalName());
evnt.addData(changedFilter.getSelectedData());
eventDispatcher.sendEvent(evnt);
}
renderData();
}
@SuppressWarnings("unchecked")
@Override
public void receiveEvent(final EventDispatcherEvent event) {
switch (event.getCommand()) {
case SELECT:
MPerspective perspective = getContext().get(MPerspective.class);
ArrayList<String> allowedSenderParts = (ArrayList<String>) eclipseContext.getActive(MPart.class).getTransientData().get(IPerspectiveProvider.EventDispatcherConstants.ACCEPTED_SENDERS);
if((event.getPerspective() == null || (perspective != null && event.getPerspective().equals(perspective))) && ((allowedSenderParts != null && allowedSenderParts.contains(event.getSender())) || allowedSenderParts == null)){
if (!event.getSender().equals(getClass().getCanonicalName())) {
for (IDatamartFilterGenerator filterGenerator : filterGeneratorMap.values()) {
if(filterGenerator.isPrimaryFilterId(event) && filterGenerator.selectItem(event, getSelectById())) {
selectedId = event.getData().get(EventDispatcherDataTag.ID);
renderData();
}
}
}
}
break;
case REFRESH:
if (!event.getSender().equals(getClass().getCanonicalName()) && matchTopic(event)) {
for(Entry<String, IDataMart> entrySet:datamartInstanceMap.entrySet()) {
entrySet.getValue().clearCache();
}
renderData();
/* must be activated if sure that the filter is to be refreshed
for (IDatamartFilterGenerator filterGenerator : filterGeneratorMap.values()) {
if (filterGenerator != null) {
filterGenerator.updateFilter();
}
}*/
}
break;
case SAVE:
case DELETE:
if (!event.getSender().equals(getClass().getCanonicalName())) {
for (LEntity entity : getEntities()) {
String entityName = EntityUtils.getFQNForLEntity(entity);
detectDeletion(event, entity, entityName);
}
}
break;
case ACTION:
if (!event.getSender().equals(getClass().getCanonicalName())) {
if (event.getTopic()!=null && event.getTopic().equals(getClass().getCanonicalName())){
Object data = event.getData().get(EventDispatcherDataTag.OBJECT);
if (data instanceof List<?>){
for (Object element : (List<?>)data){
if (element instanceof Parameter){
setParameterList((List<Parameter>)data);
}
}
}
renderData();
}
}
break;
default:
break;
}
}
private boolean matchTopic (final EventDispatcherEvent event) {
for (LEntity entity : getEntities()) {
String entityFQName = EntityUtils.getFQNForLEntity(entity);
if ((event.getTopic() != null) && (event.getTopic().equals(entityFQName))){
return true;
}
}
return false;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void detectDeletion(final EventDispatcherEvent event, LEntity entity, String entityName) {
if (entityName.equals(event.getTopic())) {
for (IDataMart datamartInstance : datamartInstanceMap.values()){
if(datamartInstance.contains(event.getData().get(EventDispatcherDataTag.ID))) {
renderData();
for (IDatamartFilterGenerator filterGenerator : filterGeneratorMap.values()) {
if (filterGenerator != null) {
filterGenerator.updateFilter();
}
}
}
}
}
}
private Set<LEntity> getEntities() {
HashSet<LEntity> entities = new HashSet<>();
for (IDataMart datamartInstance : datamartInstanceMap.values()){
DatamartDefinition datamart = (DatamartDefinition) dslMetadataService.getMetadata(
datamartInstance.getClass().getCanonicalName().replace("Datamart", ""),
DatamartDSLPackage.Literals.DATAMART_DEFINITION);
if (datamart == null) {
LOGGER.error("datamart with id " + datamartInstance.getClass().getCanonicalName() + " not found");
return entities;
}
if (datamart.getSource() instanceof DatamartEntity) {
entities.add(((DatamartEntity) datamart.getSource()).getEntityRef());
for (DatamartNavigation navigation : ((DatamartEntity) datamart.getSource()).getNavigations()) {
entities.add(navigation.getDatamartEntity().getEntityRef());
}
}
}
return entities;
}
public final void resetBean() {
filterGeneratorMap.clear();
}
public IBlobService getBlobService() {
return blobService;
}
protected abstract String getReportName();
public abstract void applyBeanFilter(Filter filter);
public abstract void applyBeanDto(Object dto);
public static class Parameter {
private String name;
private Object value;
private String displayText;
public Parameter(String name, Object value, String displayText) {
super();
this.name = name;
this.value = value;
this.displayText = displayText;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
public String getDisplayText() {
return displayText;
}
}
}