/* * Copyright (c) 2013 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.io.html; import java.awt.Dimension; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.PlatformUI; import org.eclipse.zest.core.viewers.GraphViewer; import org.eclipse.zest.core.widgets.Graph; import org.eclipse.zest.core.widgets.GraphNode; import com.google.common.io.Files; import eu.esdihumboldt.hale.common.align.io.impl.AbstractAlignmentWriter; import eu.esdihumboldt.hale.common.align.model.Alignment; import eu.esdihumboldt.hale.common.align.model.Cell; import eu.esdihumboldt.hale.common.core.io.IOProviderConfigurationException; import eu.esdihumboldt.hale.common.core.io.ProgressIndicator; import eu.esdihumboldt.hale.common.core.io.project.ProjectInfo; import eu.esdihumboldt.hale.common.core.io.project.ProjectInfoAware; import eu.esdihumboldt.hale.common.core.io.report.IOReport; import eu.esdihumboldt.hale.common.core.io.report.IOReporter; import eu.esdihumboldt.hale.common.core.io.report.impl.IOMessageImpl; import eu.esdihumboldt.hale.ui.HaleUI; import eu.esdihumboldt.hale.ui.common.graph.content.CellGraphContentProvider; import eu.esdihumboldt.hale.ui.common.graph.labels.GraphLabelProvider; import eu.esdihumboldt.hale.ui.util.DisplayThread; import eu.esdihumboldt.hale.ui.util.graph.OffscreenGraph; import eu.esdihumboldt.util.Identifiers; //import org.eclipse.draw2d.geometry.Dimension; /** * Export a Mapping to HTML for documentation purposes. * * @author Kevin Mais */ public class HtmlMappingExporter extends AbstractAlignmentWriter implements ProjectInfoAware, HtmlMappingTemplateConstants { private VelocityContext context; private VelocityEngine velocityEngine; private ProjectInfo projectInfo; private File templateFile; private File tempDir; private Alignment alignment; private Identifiers<Cell> cellIds; private IOReporter reporter; @Override public boolean isCancelable() { return false; } /** * @see eu.esdihumboldt.hale.common.core.io.project.ProjectInfoAware#setProjectInfo(eu.esdihumboldt.hale.common.core.io.project.ProjectInfo) */ @Override public void setProjectInfo(ProjectInfo projectInfo) { this.projectInfo = projectInfo; } @Override protected String getDefaultTypeName() { return "HTML mapping documentation"; } @Override protected IOReport execute(ProgressIndicator progress, IOReporter reporter) throws IOProviderConfigurationException, IOException { this.reporter = reporter; context = new VelocityContext(); cellIds = new Identifiers<Cell>(Cell.class, false); alignment = getAlignment(); URL headlinePath = this.getClass().getResource("bg-headline.png"); //$NON-NLS-1$ URL cssPath = this.getClass().getResource("style.css"); //$NON-NLS-1$ URL linkPath = this.getClass().getResource("int_link.png"); //$NON-NLS-1$ URL tooltipIcon = this.getClass().getResource("tooltip.gif"); //$NON-NLS-1$ final String filesSubDir = FilenameUtils.removeExtension(FilenameUtils.getName(getTarget() .getLocation().getPath())) + "_files"; //$NON-NLS-1$ final File filesDir = new File(FilenameUtils.getFullPath(getTarget().getLocation() .getPath()), filesSubDir); filesDir.mkdirs(); context.put(FILE_DIRECTORY, filesSubDir); try { init(); } catch (Exception e) { return reportError(reporter, "Initializing error", e); } File cssOutputFile = new File(filesDir, "style.css"); FileUtils.copyFile(getInputFile(cssPath), cssOutputFile); // create headline picture File headlineOutputFile = new File(filesDir, "bg-headline.png"); //$NON-NLS-1$ FileUtils.copyFile(getInputFile(headlinePath), headlineOutputFile); File linkOutputFile = new File(filesDir, "int_link.png"); //$NON-NLS-1$ FileUtils.copyFile(getInputFile(linkPath), linkOutputFile); File tooltipIconFile = new File(filesDir, "tooltip.png"); //$NON-NLS-1$ FileUtils.copyFile(getInputFile(tooltipIcon), tooltipIconFile); File htmlExportFile = new File(getTarget().getLocation().getPath()); if (projectInfo != null) { Date date = new Date(); DateFormat dfm = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT); // associate variables with information data String exportDate = dfm.format(date); context.put(EXPORT_DATE, exportDate); if (projectInfo.getCreated() != null) { String created = dfm.format(projectInfo.getCreated()); context.put(CREATED_DATE, created); } context.put(PROJECT_INFO, projectInfo); } if (alignment != null) { Collection<TypeCellInfo> typeCellInfos = new ArrayList<TypeCellInfo>(); Collection<? extends Cell> cells = alignment.getTypeCells(); Iterator<? extends Cell> it = cells.iterator(); while (it.hasNext()) { final Cell cell = it.next(); // this is the collection of type cell info TypeCellInfo typeCellInfo = new TypeCellInfo(cell, alignment, cellIds, filesSubDir); typeCellInfos.add(typeCellInfo); } // put the full collection of type cell info to the context (for the // template) context.put(TYPE_CELL_INFOS, typeCellInfos); createImages(filesDir); } context.put(TOOLTIP, getParameter(TOOLTIP).as(boolean.class)); Template template; try { template = velocityEngine.getTemplate(templateFile.getName(), "UTF-8"); } catch (Exception e) { return reportError(reporter, "Could not load template", e); } FileWriter fileWriter = new FileWriter(htmlExportFile); template.merge(context, fileWriter); fileWriter.close(); reporter.setSuccess(true); return reporter; } /** * Initialize temporary directory and template engine. * * @throws Exception if an error occurs during the initialization */ private void init() throws Exception { synchronized (this) { if (velocityEngine == null) { velocityEngine = new VelocityEngine(); // create a temporary directory tempDir = Files.createTempDir(); templateFile = new File(tempDir, "template.vm"); URL templatePath = getClass().getResource("template.html"); OutputStream outputStream = new BufferedOutputStream(new FileOutputStream( templateFile)); InputStream inputStream = templatePath.openStream(); // copies the InputStream into OutputStream IOUtils.copy(inputStream, outputStream); inputStream.close(); outputStream.close(); tempDir.deleteOnExit(); templateFile.deleteOnExit(); velocityEngine.setProperty("file.resource.loader.path", tempDir.getAbsolutePath()); // initialize VelocityEngine velocityEngine.init(); } } } private void createImages(File filesDir) { Collection<? extends Cell> cells = alignment.getCells(); Iterator<? extends Cell> ite = cells.iterator(); while (ite.hasNext()) { Cell cell = ite.next(); saveImageToFile(cell, filesDir); } } private void saveImageToFile(final Cell cell, File filesDir) { Display display; if (Display.getCurrent() != null) { // use the current display if available display = Display.getCurrent(); } else { try { // use workbench display if available display = PlatformUI.getWorkbench().getDisplay(); } catch (Throwable e) { // use a dedicated display thread if no workbench is // available display = DisplayThread.getInstance().getDisplay(); } } // creates a unique id for each cell String cellId = cellIds.getId(cell); final File file = new File(filesDir, "img_" + cellId + ".png"); display.syncExec(new Runnable() { @Override public void run() { OffscreenGraph offscreenGraph = new OffscreenGraph(600, 200) { @Override protected void configureViewer(GraphViewer viewer) { IContentProvider contentProvider = new CellGraphContentProvider(); GraphLabelProvider labelProvider = new GraphLabelProvider(null, HaleUI .getServiceProvider()); viewer.setContentProvider(contentProvider); viewer.setLabelProvider(labelProvider); viewer.setInput(cell); } }; Graph graph = offscreenGraph.getGraph(); Dimension dimension = computeSize(graph); // minimum width = 600 offscreenGraph.resize(dimension.width > 600 ? dimension.width : 600, dimension.height); try { offscreenGraph.saveImage(new BufferedOutputStream(new FileOutputStream(file)), null); } catch (Exception e) { reporter.error(new IOMessageImpl("Can not create image", e)); } finally { offscreenGraph.dispose(); } } }); } private Dimension computeSize(Graph graph) { @SuppressWarnings("unchecked") List<GraphNode> graphNodes = graph.getNodes(); int height = 0; int width = 0; List<GraphNode> tempSourceList = new ArrayList<GraphNode>(); List<GraphNode> tempTargetList = new ArrayList<GraphNode>(); for (GraphNode node : graphNodes) { int sourceConnections = node.getSourceConnections().size(); int targetConnections = node.getTargetConnections().size(); if (sourceConnections == 0 && targetConnections == 1) { tempSourceList.add(node); } else if (sourceConnections >= 1 && targetConnections >= 1) { width = width + node.getFigure().getBounds().width + 10; height = height + node.getFigure().getBounds().height; } else { tempTargetList.add(node); } } int accuSourceWidth = 0; int accuSourceHeight = 0; int accuHeight = 0; for (GraphNode node : tempSourceList) { Rectangle rec = node.getFigure().getBounds(); int sourceWidth = rec.width; int sourceHeight = rec.height; accuSourceHeight = accuSourceHeight + sourceHeight + 10; if (accuSourceWidth < sourceWidth) { accuSourceWidth = sourceWidth; } if (accuHeight < accuSourceHeight) { accuHeight = accuSourceHeight; } } int accuTargetWidth = 0; int accuTargetHeight = 0; for (GraphNode node : tempTargetList) { Rectangle rec = node.getFigure().getBounds(); int targetWidth = rec.width; int targetHeight = rec.height; accuTargetHeight = accuTargetHeight + targetHeight + 10; if (accuTargetWidth < targetWidth) { accuTargetWidth = targetWidth; } if (accuHeight < accuTargetHeight) { accuHeight = accuTargetHeight; } } width = width + accuSourceWidth + accuTargetWidth + 30; height = accuHeight + 15; Dimension dimension = new Dimension(); dimension.setSize(width, height); return dimension; } private File getInputFile(URL url) throws IOException, FileNotFoundException { File file = new File(tempDir.toString() + FilenameUtils.getName(url.toString())); OutputStream outputStream = new FileOutputStream(file); IOUtils.copy(url.openStream(), outputStream); outputStream.close(); return file; } private IOReport reportError(IOReporter reporter, String message, Exception e) { reporter.error(new IOMessageImpl(message, e)); reporter.setSuccess(false); return reporter; } }