Skip to content

Content of file EMFFormsSpreadsheetControlRenderer.java

/*******************************************************************************
 * Copyright (c) 2011-2015 EclipseSource Muenchen GmbH and others.
 *
 * 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:
 * Eugen Neufeld - initial API and implementation
 * Johannes Faltermeier - template pattern for extenders
 ******************************************************************************/
package org.eclipse.emfforms.internal.spreadsheet.core.renderer;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Set;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter.WriteableOutputStream;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
import org.eclipse.emf.ecp.view.spi.indexdmr.model.VIndexDomainModelReference;
import org.eclipse.emf.ecp.view.spi.model.VControl;
import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
import org.eclipse.emf.ecp.view.spi.model.VElement;
import org.eclipse.emf.ecp.view.spi.model.VView;
import org.eclipse.emf.ecp.view.template.model.VTStyleProperty;
import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider;
import org.eclipse.emf.ecp.view.template.style.mandatory.model.VTMandatoryFactory;
import org.eclipse.emf.ecp.view.template.style.mandatory.model.VTMandatoryStyleProperty;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
import org.eclipse.emfforms.spi.core.services.databinding.emf.EMFFormsDatabindingEMF;
import org.eclipse.emfforms.spi.core.services.domainexpander.EMFFormsDomainExpander;
import org.eclipse.emfforms.spi.core.services.domainexpander.EMFFormsExpandingFailedException;
import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider;
import org.eclipse.emfforms.spi.core.services.label.NoLabelFoundException;
import org.eclipse.emfforms.spi.spreadsheet.core.EMFFormsAbstractSpreadsheetRenderer;
import org.eclipse.emfforms.spi.spreadsheet.core.EMFFormsExportTableParent;
import org.eclipse.emfforms.spi.spreadsheet.core.EMFFormsIdProvider;
import org.eclipse.emfforms.spi.spreadsheet.core.EMFFormsSpreadsheetFormatDescriptionProvider;
import org.eclipse.emfforms.spi.spreadsheet.core.EMFFormsSpreadsheetRenderTarget;
import org.eclipse.emfforms.spi.spreadsheet.core.EMFFormsSpreadsheetReport;
import org.eclipse.emfforms.spi.spreadsheet.core.converter.EMFFormsCellStyleConstants;
import org.eclipse.emfforms.spi.spreadsheet.core.converter.EMFFormsConverterException;
import org.eclipse.emfforms.spi.spreadsheet.core.converter.EMFFormsSpreadsheetValueConverter;
import org.eclipse.emfforms.spi.spreadsheet.core.converter.EMFFormsSpreadsheetValueConverterRegistry;

/**
 * Spreadsheet renderer for {@link VControl}.
 *
 * @author Eugen Neufeld
 * @author Johannes Faltermeier
 */
public class EMFFormsSpreadsheetControlRenderer extends EMFFormsAbstractSpreadsheetRenderer<VControl> {

	private final EMFFormsDatabindingEMF emfformsDatabinding;
	private final EMFFormsLabelProvider emfformsLabelProvider;
	private final ReportService reportService;
	private final VTViewTemplateProvider vtViewTemplateProvider;
	private final EMFFormsIdProvider idProvider;
	private final EMFFormsSpreadsheetValueConverterRegistry converterRegistry;
	private final EMFFormsSpreadsheetFormatDescriptionProvider formatDescriptionProvider;
	private final EMFFormsDomainExpander domainExpander;

	/**
	 * Default constructor.
	 *
	 * @param emfformsDatabinding The EMFFormsDatabinding to use
	 * @param emfformsLabelProvider The EMFFormsLabelProvider to use
	 * @param reportService The {@link ReportService}
	 * @param vtViewTemplateProvider The {@link VTViewTemplateProvider}
	 * @param idProvider The {@link EMFFormsIdProvider}
	 * @param converterRegistry The {@link EMFFormsSpreadsheetValueConverterRegistry}
	 * @param formatDescriptionProvider The {@link EMFFormsSpreadsheetFormatDescriptionProvider}
	 * @param domainExpander The {@link EMFFormsDomainExpander}
	 */
	// BEGIN COMPLEX CODE
	public EMFFormsSpreadsheetControlRenderer(
		EMFFormsDatabindingEMF emfformsDatabinding,
		EMFFormsLabelProvider emfformsLabelProvider,
		ReportService reportService,
		VTViewTemplateProvider vtViewTemplateProvider,
		EMFFormsIdProvider idProvider,
		EMFFormsSpreadsheetValueConverterRegistry converterRegistry,
		EMFFormsSpreadsheetFormatDescriptionProvider formatDescriptionProvider,
		EMFFormsDomainExpander domainExpander) {
		this.emfformsDatabinding = emfformsDatabinding;
		this.emfformsLabelProvider = emfformsLabelProvider;
		this.reportService = reportService;
		this.vtViewTemplateProvider = vtViewTemplateProvider;
		this.idProvider = idProvider;
		this.converterRegistry = converterRegistry;
		this.formatDescriptionProvider = formatDescriptionProvider;
		this.domainExpander = domainExpander;
	}

	// END COMPLEX CODE
	/**
	 * {@inheritDoc}
	 *
	 * @see org.eclipse.emfforms.spi.spreadsheet.core.EMFFormsAbstractSpreadsheetRenderer#render(org.apache.poi.ss.usermodel.Workbook,
	 *      org.eclipse.emf.ecp.view.spi.model.VElement, org.eclipse.emf.ecp.view.spi.context.ViewModelContext,
	 *      org.eclipse.emfforms.spi.spreadsheet.core.EMFFormsSpreadsheetRenderTarget)
	 */
	@Override
	public int render(Workbook workbook, VControl vElement,
		ViewModelContext viewModelContext, EMFFormsSpreadsheetRenderTarget renderTarget) {
		Sheet sheet = workbook.getSheet(renderTarget.getSheetName());
		if (sheet == null) {
			sheet = workbook.createSheet(renderTarget.getSheetName());
			setupSheetFormat(sheet);
		}
		Row labelRow = sheet.getRow(0);
		if (labelRow == null) {
			labelRow = sheet.createRow(0);
		}
		Row descriptionRow = sheet.getRow(1);
		if (descriptionRow == null) {
			descriptionRow = sheet.createRow(1);
		}
		Row formatRow = sheet.getRow(2);
		if (formatRow == null) {
			formatRow = sheet.createRow(2);
		}

		final CellStyle readOnly = (CellStyle) viewModelContext.getContextValue(EMFFormsCellStyleConstants.LOCKED);
		final CellStyle readOnlyWrap = (CellStyle) viewModelContext
			.getContextValue(EMFFormsCellStyleConstants.LOCKED_AND_WRAPPED);

		final Cell idCell = labelRow.getCell(0, Row.CREATE_NULL_AS_BLANK);
		idCell.setCellValue(EMFFormsIdProvider.ID_COLUMN);
		idCell.setCellStyle(readOnly);

		final Cell labelCell = labelRow.getCell(renderTarget.getColumn() + 1,
			Row.CREATE_NULL_AS_BLANK);
		labelCell.setCellStyle(readOnlyWrap);

		final Cell descriptionCell = descriptionRow.getCell(renderTarget.getColumn() + 1,
			Row.CREATE_NULL_AS_BLANK);
		descriptionCell.setCellStyle(readOnlyWrap);

		final Cell formatCell = formatRow.getCell(renderTarget.getColumn() + 1,
			Row.CREATE_NULL_AS_BLANK);
		formatCell.setCellStyle(readOnlyWrap);

		try {
			final EMFFormsExportTableParent exportTableParent = (EMFFormsExportTableParent) viewModelContext
				.getContextValue(EMFFormsExportTableParent.EXPORT_TABLE_PARENT);
			VDomainModelReference dmrToResolve = EcoreUtil.copy(vElement.getDomainModelReference());
			if (exportTableParent != null) {
				final VIndexDomainModelReference indexDMR = exportTableParent.getIndexDMRToExtend();
				indexDMR.setTargetDMR(dmrToResolve);

				dmrToResolve = exportTableParent.getIndexDMRToResolve();
			}

			if (labelCell.getCellComment() == null) {
				final EStructuralFeature structuralFeature = emfformsDatabinding.getValueProperty(
					dmrToResolve, viewModelContext.getDomainModel()).getStructuralFeature();

				writeLabel(vElement, viewModelContext, labelCell, exportTableParent, dmrToResolve, structuralFeature);

				final Comment comment = createComment(workbook, sheet, dmrToResolve,
					renderTarget.getRow(), renderTarget.getColumn() + 1);
				labelCell.setCellComment(comment);

				writeDescription(viewModelContext, descriptionCell, dmrToResolve);

				writeFormatInformation(formatCell, structuralFeature);
			}
			if (viewModelContext.getDomainModel() != null) {
				writeValue(viewModelContext, renderTarget, sheet, dmrToResolve);
			}

			return 1;
		} catch (final DatabindingFailedException ex) {
			reportService.report(new EMFFormsSpreadsheetReport(ex, EMFFormsSpreadsheetReport.ERROR));
		} catch (final NoLabelFoundException ex) {
			reportService.report(new EMFFormsSpreadsheetReport(ex, EMFFormsSpreadsheetReport.ERROR));
		} catch (final IOException ex) {
			reportService.report(new EMFFormsSpreadsheetReport(ex, EMFFormsSpreadsheetReport.ERROR));
		} catch (final EMFFormsConverterException ex) {
			reportService.report(new EMFFormsSpreadsheetReport(ex, EMFFormsSpreadsheetReport.ERROR));
		}

		return 0;
	}

	private void writeValue(ViewModelContext viewModelContext, EMFFormsSpreadsheetRenderTarget renderTarget,
		Sheet sheet, VDomainModelReference dmrToResolve) throws DatabindingFailedException, EMFFormsConverterException {
		Row valueRow = sheet.getRow(renderTarget.getRow() + 3);
		if (valueRow == null) {
			valueRow = sheet.createRow(renderTarget.getRow() + 3);
		}
		valueRow.getCell(0, Row.CREATE_NULL_AS_BLANK)
			.setCellValue(idProvider.getId(viewModelContext.getDomainModel()));

		try {
			expandDMR(dmrToResolve, viewModelContext.getDomainModel());
		} catch (final EMFFormsExpandingFailedException ex) {
			reportService.report(new EMFFormsSpreadsheetReport(ex, EMFFormsSpreadsheetReport.ERROR));
			return;
		}
		final Setting setting = emfformsDatabinding.getSetting(dmrToResolve, viewModelContext.getDomainModel());

		/* only create new cells for non-unsettable features and unsettable feature which are set */
		/*
		 * if the eObject is null, this means that the dmr could not be resolved correctly. in this case we want
		 * to create an empty cell
		 */
		if (setting.getEObject() == null || !setting.getEStructuralFeature().isUnsettable()
			|| setting.getEStructuralFeature().isUnsettable() && setting.isSet()) {
			final Object value = setting.get(true);
			final EMFFormsSpreadsheetValueConverter converter = converterRegistry
				.getConverter(viewModelContext.getDomainModel(), dmrToResolve);

			final Cell valueCell = valueRow.getCell(renderTarget.getColumn() + 1,
				Row.CREATE_NULL_AS_BLANK);
			converter.setCellValue(valueCell, value, setting.getEStructuralFeature(), viewModelContext);

		}
	}

	private void expandDMR(VDomainModelReference dmrToResolve, EObject domainModel)
		throws EMFFormsExpandingFailedException {
		domainExpander.prepareDomainObject(dmrToResolve, domainModel);
	}

	private void writeLabel(VControl vControl, ViewModelContext viewModelContext, final Cell labelCell,
		final EMFFormsExportTableParent exportTableParent, VDomainModelReference dmrToResolve,
		final EStructuralFeature structuralFeature) throws NoLabelFoundException {
		IObservableValue displayName;
if (viewModelContext.getDomainModel() != null) { displayName = emfformsLabelProvider.getDisplayName(dmrToResolve, viewModelContext.getDomainModel()); } else { final VView view = (VView) viewModelContext.getViewModel(); displayName = emfformsLabelProvider.getDisplayName(dmrToResolve, view.getRootEClass()); } String labelValue = displayName.getValue().toString(); if (exportTableParent != null) { labelValue = exportTableParent.getLabelPrefix() + "_" + labelValue; //$NON-NLS-1$ } String extra = ""; //$NON-NLS-1$ final VTMandatoryStyleProperty mandatoryStyle = getMandatoryStyle(vControl, viewModelContext); if (mandatoryStyle.isHighliteMandatoryFields() && structuralFeature.getLowerBound() > 0) { extra = mandatoryStyle.getMandatoryMarker(); } labelValue = labelValue + extra; labelCell.setCellValue(labelValue); displayName.dispose(); } private void writeDescription(ViewModelContext viewModelContext, final Cell descriptionCell, VDomainModelReference dmrToResolve) throws NoLabelFoundException { IObservableValue description; if (viewModelContext.getDomainModel() != null) { description = emfformsLabelProvider.getDescription(dmrToResolve, viewModelContext.getDomainModel()); } else { final VView view = (VView) viewModelContext.getViewModel(); description = emfformsLabelProvider.getDescription(dmrToResolve, view.getRootEClass()); } descriptionCell.setCellValue(description.getValue().toString()); description.dispose(); } private void writeFormatInformation(final Cell formatCell, final EStructuralFeature structuralFeature) { String format = ""; //$NON-NLS-1$ if (formatDescriptionProvider != null) { format = formatDescriptionProvider.getFormatDescription(structuralFeature); } formatCell.setCellValue(format); } private static void setupSheetFormat(final Sheet sheet) { sheet.setDefaultColumnWidth(30); // do not scroll the first column (id) and the three top rows (label+info) sheet.createFreezePane(1, 3); } private VTMandatoryStyleProperty getMandatoryStyle(VElement vElement, ViewModelContext viewModelContext) { if (vtViewTemplateProvider == null) { return getDefaultStyle(); } final Set<VTStyleProperty> styleProperties = vtViewTemplateProvider .getStyleProperties(vElement, viewModelContext); for (final VTStyleProperty styleProperty : styleProperties) { if (VTMandatoryStyleProperty.class.isInstance(styleProperty)) { return (VTMandatoryStyleProperty) styleProperty; } } return getDefaultStyle(); } private VTMandatoryStyleProperty getDefaultStyle() { return VTMandatoryFactory.eINSTANCE.createMandatoryStyleProperty(); } private Comment createComment(Workbook workbook, Sheet sheet, VDomainModelReference domainModelReference, int row, int column) throws IOException { final CreationHelper factory = workbook.getCreationHelper(); // When the comment box is visible, have it show in a 1x3 space final ClientAnchor anchor = factory.createClientAnchor(); anchor.setCol1(column); anchor.setCol2(column + 1); anchor.setRow1(row); anchor.setRow2(row + 1); final Drawing drawing = sheet.createDrawingPatriarch(); final Comment comment = drawing.createCellComment(anchor); comment.setAuthor("EMFForms Spreadsheet Renderer"); //$NON-NLS-1$ comment.setVisible(false); comment.setString(factory.createRichTextString(getSerializedDMR(domainModelReference))); return comment; } private String getSerializedDMR(VDomainModelReference domainModelReference) throws IOException { final ResourceSet rs = new ResourceSetImpl(); final Resource resource = rs.createResource(URI.createURI("VIRTAUAL_URI")); //$NON-NLS-1$ resource.getContents().add(EcoreUtil.copy(domainModelReference)); final StringWriter sw = new StringWriter(); final WriteableOutputStream os = new WriteableOutputStream(sw, "UTF-8"); //$NON-NLS-1$ resource.save(os, null); final String value = sw.getBuffer().toString(); return value; } }