Package org.openiaml.model.diagram.custom.actions

Source Code of org.openiaml.model.diagram.custom.actions.ExportToClickableHtml$EObjectDepthComparator

package org.openiaml.model.diagram.custom.actions;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.transaction.impl.InternalTransactionalEditingDomain;
import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.image.ImageFileFormat;
import org.eclipse.gmf.runtime.diagram.ui.render.clipboard.DiagramGenerator;
import org.eclipse.gmf.runtime.diagram.ui.render.util.CopyToImageUtil;
import org.eclipse.gmf.runtime.diagram.ui.render.util.DiagramImageUtils;
import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode;
import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil;
import org.openiaml.model.helpers.IamlBreadcrumb;
import org.openiaml.model.helpers.IamlBreadcrumb.BreadcrumbLinker;


/**
* Export all of the images in a model diagram to multiple image files, and
* save all of these images as clickable HTML.
*
* @author jmwright
*
*/
public class ExportToClickableHtml extends ExportImagePartsAction {
 
  private static final int LATEX_BREADCRUMB_DEPTH = 2;
  private static final int HTML_BREADCRUMB_DEPTH = 4;

  /**
   * A Comparator that sorts based firstly on depth to the object root, and then
   * on the breadcrumb String of the objects.
   *
   * @author jmwright
   * @see #getDepth(EObject)
   * @see IamlBreadcrumb#breadcrumb(EObject, int)
   */
  public class EObjectDepthComparator implements Comparator<EObject> {

    /**
     * Get the depth of the object. Limits to 100.
     */
    private int getDepth(EObject o) {
      int depth = 0;
      while (o.eContainer() != null && depth < 100) {
        o = o.eContainer();
        depth++;
      }
      return depth;
    }
   
    @Override
    public int compare(EObject o1, EObject o2) {
      Integer depth1 = getDepth(o1);
      Integer depth2 = getDepth(o2);
     
      if (depth1.compareTo(depth2) != 0) {
        return depth1.compareTo(depth2);
      }
     
      String bread1 = IamlBreadcrumb.breadcrumb(o1, LATEX_BREADCRUMB_DEPTH);
      String bread2 = IamlBreadcrumb.breadcrumb(o2, LATEX_BREADCRUMB_DEPTH);
     
      return bread1.compareTo(bread2);
    }

  }

  /**
   * We want to render much more of the entire model; we
   * render up to 500 images.
   */
  @Override
  public int getMaxImages() {
    return 500;
  }

  /**
   * Keeps track of (source edit parts -> destination files)
   */
  private Map<DiagramEditPart,IPath> partDestinationMap;
 
  private Map<DiagramEditPart,Rectangle> partRectangleMap;
 
  private Map<DiagramEditPart,EObject> partEObjectMap;
 
  /**
   * Stores a list of all the resolved EObjects of children
   */
  private Map<DiagramEditPart,List<RenderedChildInformation>> partChildrenListMap =
    new HashMap<DiagramEditPart,List<RenderedChildInformation>>();
 
  public class RenderedChildInformation {
    private EObject resolvedObject;
    private Rectangle bounds;
    private String tooltip;
   
    public RenderedChildInformation(EObject resolvedObject, Rectangle bounds, String tooltip) {
      super();
      this.resolvedObject = resolvedObject;
      this.bounds = bounds;
      this.tooltip = tooltip;
    }
   
    public EObject getResolvedObject() {
      return resolvedObject;
    }
    public Rectangle getBounds() {
      return bounds;
    }
    public String getTooltip() {
      return tooltip;
    }
   
  }
 
  public class MyCopyToImageUtil extends CopyToImageUtil {

    @Override
    public DiagramGenerator copyToImage(DiagramEditPart part,
        IPath destination, ImageFileFormat format,
        IProgressMonitor monitor) throws CoreException {
      DiagramGenerator result = super.copyToImage(part, destination, format, monitor);
     
      // keep track of the part -> destination
      partDestinationMap.put(part, destination);
      partEObjectMap.put(part, part.resolveSemanticElement());
     
      // make a copy of the image rectangle used
      // we do this before we close the editor, so that we don't lose the editor instance/children
      Rectangle rect = DiagramImageUtils.calculateImageRectangle(
          part.getPrimaryEditParts(),
          getImageMargin(part),
          getEmptyImageSize(part));
      partRectangleMap.put(part, rect);
     
      // save all the children now
      List<RenderedChildInformation> children = new ArrayList<RenderedChildInformation>();
      for (Object o : part.getChildren()) {
        if (o instanceof GraphicalEditPart) {
          GraphicalEditPart gep = (GraphicalEditPart) o;
         
          RenderedChildInformation info = new RenderedChildInformation(
              gep.resolveSemanticElement(),
              gep.getContentPane().getBounds(),
              IamlBreadcrumb.getEObjectBreadcrumbString(gep.resolveSemanticElement()) );
          children.add(info);
        }
      }
      partChildrenListMap.put(part, children);
     
      return result;
    }
   
  }

  /**
   * We extend CopyToImageUtil to also keep a track of all
   * clickable elements in the current edit part.
   */
  @Override
  protected CopyToImageUtil getCopyToImageUtil() {
    return new MyCopyToImageUtil();
  }

  private IFile targetDiagram;
 
  /**
   * Once we have finished exporting all of the images, we go through
   * every saved node:
   *
   * <ol>
   *   <li>Export a HTML file for the current part</li>
   <li>Find all children</li>
   <li>Find if any exported image parts correspond to the target element</li>
   <li>If so, write out a clickable map region corresponding to the target HTML</li>
   * </ol>
   *
   * @param container the container to place the exported images and HTML into, or <code>null</code> to place in the same container as <code>targetDiagram</code>
   */
  @Override
  public void doExport(IFile targetDiagram, IContainer container, IProgressMonitor monitor)
      throws ExportImageException {
   
    monitor.beginTask("Exporting to HTML", 110);
    this.targetDiagram = targetDiagram;
   
    // initialise maps
    partDestinationMap = new HashMap<DiagramEditPart,IPath>();
    partRectangleMap = new HashMap<DiagramEditPart,Rectangle>();
    partEObjectMap = new HashMap<DiagramEditPart,EObject>();
   
    // for latex export
    Map<EObject, String> latexMap = new HashMap<EObject, String>();
   
    // do parent
    super.doExport(targetDiagram, container, new SubProgressMonitor(monitor, 70));
   
    IProgressMonitor finalMonitor = new SubProgressMonitor(monitor, 30);
    finalMonitor.beginTask("Writing HTML files", partDestinationMap.size() * 2);
   
    // get all image parts
    for (DiagramEditPart root : partDestinationMap.keySet()) {
      if (monitor.isCanceled())
        return;
     
      IPath destination = partDestinationMap.get(root);
      IPath htmlDestination = getHTMLDestinationFor(destination);
      EObject resolved = partEObjectMap.get(root);
      finalMonitor.subTask("Writing " + htmlDestination.lastSegment());
     
      // construct the HTML
      StringBuffer html = new StringBuffer();
      html.append(getHTMLHeader(targetDiagram, resolved))
        .append(getBreadcrumb(resolved))
        .append(getImageTag(destination))
        .append(getClickableMap(root))
        .append(getHTMLFooter());
     
      // add the SVG
      latexMap.put(resolved, getLatexTag(resolved, destination));
     
      finalMonitor.worked(1);
     
      // export HTML page
      try {
        File destFile = new File(htmlDestination.toOSString());
        FileWriter fw = new FileWriter(destFile);
        fw.write(html.toString());
        fw.close();
      } catch (IOException e) {
        throw new ExportImageException("Could not export individual HTML page '" + htmlDestination + "': " + e.getMessage(), e);
      }
      finalMonitor.worked(1);
    }
   
    // export Latex page
    IPath latexDestination = targetDiagram.getLocation().removeFileExtension().addFileExtension("tex");
    try {
      File destFile = new File(latexDestination.toOSString());
      FileWriter fw = new FileWriter(destFile);
     
      // we want to sort the latex output based on EObject depth
      List<EObject> objectList = new ArrayList<EObject>(latexMap.keySet());
      Collections.sort(objectList, new EObjectDepthComparator());
      for (EObject obj : objectList) {
        fw.write(latexMap.get(obj));
      }
      fw.close();
      finalMonitor.worked(5);
    } catch (IOException e) {
      throw new ExportImageException("Could not export Latex page '" + latexDestination + "': " + e.getMessage(), e);
    }
   
    finalMonitor.done();
   
    // once finished, refresh parent (folder, project)
    try {
      targetDiagram.getParent().refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 5));
    } catch (CoreException e) {
      throw new ExportImageException(e);
    }

    // all finished
    monitor.done();
  }
 
  /**
   * We want to place all generated files into a new folder.
   *
   * @param container the container to place images into
   * @throws ExportImageException
   */
  @Override
  protected IPath generateImageDestination(IContainer container, ImageFileFormat format) throws ExportImageException {

    // get default
    IPath source = super.generateImageDestination(container, format);
   
    // does the folder exist?
    // get the folder name from the diagram, e.g. Foo.iaml_diagram -> "Foo"
    String newFolderName = targetDiagram.getFullPath().removeFileExtension().lastSegment();
   
    IPath targetFolder = source.removeLastSegments(1).append(newFolderName).addTrailingSeparator();
    File f = new File(targetFolder.toOSString());
    if (!f.exists()) {
      if (!f.mkdir()) {
        // could not create directory
        throw new ExportImageException("Could not mkdir: '" + f + "'");
      }
    }
   
    // create the new path
    return targetFolder.append(source.lastSegment());

  }

  /**
   * Allows linking of breadcrumb elements to target pages, if they
   * have been rendered.
   *
   * @author jmwright
   *
   */
  public class HtmlBreadcrumbLinker extends BreadcrumbLinker {

    private Map<DiagramEditPart, IPath> partDestinationMap;

    public HtmlBreadcrumbLinker(
        Map<DiagramEditPart, IPath> partDestinationMap) {
      this.partDestinationMap = partDestinationMap;
    }

    @Override
    public String link(EObject object, String s) {
      // does the given EObject resolve to any target in the
      // rendered map?
     
      for (DiagramEditPart part : partDestinationMap.keySet()) {
        // try and prevent an NPE caused by internal getChangeRecorder() returning null
        if (part.getEditingDomain() instanceof InternalTransactionalEditingDomain) {
          if (((InternalTransactionalEditingDomain) part.getEditingDomain()).getChangeRecorder() == null) {
            continue;
          }
        }

        EObject resolved;
        try {
          resolved = part.resolveSemanticElement();
        } catch (RuntimeException e) {
          throw new RuntimeException("Could not resolve element for part '" + part + "': " + e.getMessage(), e);
        }
       
        if (EcoreUtil.equals(resolved, object)) {
          // it maps
          return new StringBuffer()
            .append("<a href=\"")
            .append(getHTMLDestinationFor(partDestinationMap.get(part)).lastSegment())
            .append("\">")
            .append(s)
            .append("</a>").toString();
        }
      }
     
      // return default
      return s;
    }
   
  }
 
  /**
   * Get a linkable breadcrumb.
   *
   * @param destination
   * @return
   */
  private String getBreadcrumb(EObject resolved) {
    return new StringBuffer()
    .append("<h2>")
    .append(IamlBreadcrumb.breadcrumb(resolved, HTML_BREADCRUMB_DEPTH, new HtmlBreadcrumbLinker(partDestinationMap) /* HTML linker */))
    .append("</h2>\n")
    .toString();
  }
 
  /**
   * Get the latex output for the given element, which has been generated to the given
   * image path. The file extension for the image is also removed.
   *
   * @param destination
   * @return
   */
  private String getLatexTag(EObject element, IPath image) {
    String breadcrumb = IamlBreadcrumb.breadcrumb(element, LATEX_BREADCRUMB_DEPTH /* no linker */);
    return "\\exportedImage{" + breadcrumb + "}{" + image.removeFileExtension().lastSegment() + "}\n";
  }

  /**
   * For a given path (e.g. "foo.png"), get the HTML file
   * that will render this (e.g. "foo.html").
   *
   * @param source
   * @return
   */
  protected IPath getHTMLDestinationFor(IPath source) {
    return source.removeFileExtension().addFileExtension("html");
  }

  /**
   * Construct the map.
   *
   * @return
   */
  protected String getClickableMap(DiagramEditPart root) {
    StringBuffer buf = new StringBuffer();
    buf.append("<map name=\"generated_map\">\n");

    // we need to find out the source rect that was taken
    // to construct the image, so we can base the bounds
    // on this minimal bound
    Rectangle rect = partRectangleMap.get(root);
   
    // for all the saved children
    for (RenderedChildInformation child : partChildrenListMap.get(root)) {
     
      // do any edit parts link up to this one?
      DiagramEditPart found = null;
      for (DiagramEditPart target : partEObjectMap.keySet()) {
        if (EcoreUtil.equals(partEObjectMap.get(target), child.getResolvedObject())) {
          found = target;
          break;
        }
      }
     
      if (found != null) {
        // found a link
        // assume the link is square
       
        Rectangle bounds = child.getBounds();
        IPath dest = partDestinationMap.get(found);
       
        buf.append("<area shape=\"rect\" coords=\"")
          .append(bounds.x-rect.x).append(",")
          .append(bounds.y-rect.y).append(",")
          .append(bounds.width+bounds.x-rect.x).append(",")
          .append(bounds.height+bounds.y-rect.y)
          .append("\" href=\"")
          .append(escapeHTML(getHTMLDestinationFor(dest).lastSegment()))
          .append("\" alt=\"")
          .append(escapeHTML(child.getTooltip()))
          .append("\" title=\"")
          .append(escapeHTML(child.getTooltip()))
          .append("\" />\n");
      } else {
        // we can still add tooltip information anyway

        Rectangle bounds = child.getBounds();
       
        buf.append("<area shape=\"rect\" coords=\"")
          .append(bounds.x-rect.x).append(",")
          .append(bounds.y-rect.y).append(",")
          .append(bounds.width+bounds.x-rect.x).append(",")
          .append(bounds.height+bounds.y-rect.y)
          .append("\" alt=\"")
          .append(escapeHTML(child.getTooltip()))
          .append("\" title=\"")
          .append(escapeHTML(child.getTooltip()))
          .append("\" />\n");
     
      }
     
    }
   
    buf.append("</map>");
    return buf.toString();
  }

  /**
   * Escape the given HTML.
   */
  protected String escapeHTML(String s) {
    return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;");
  }
 
  /**
   * Get the HTML title for the given object.
   *
   * @param targetDiagram the source (root) diagram
   * @param part the current EObject to render a title for
   * @return
   */
  protected String getHTMLTitle(IFile targetDiagram, EObject part) {
    String filename = targetDiagram.getProjectRelativePath().lastSegment();
    return new StringBuffer()
      .append(escapeHTML(filename))
      .append(" : ")
      .append(escapeHTML(IamlBreadcrumb.getEObjectBreadcrumbString(part)))
      .toString();
  }
 
  /**
   * Get the HTML header.
   *
   * @param targetDiagram the source (root) diagram
   * @param part the current part to render a title for
   * @return
   */
  protected String getHTMLHeader(IFile targetDiagram, EObject part) {
    return new StringBuffer()
      .append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n")
      .append("\"http://www.w3.org/TR/html4/loose.dtd\">\n")
      .append("<html><head>\n")
      .append("<title>")
      .append(getHTMLTitle(targetDiagram, part))
      .append("</title>\n")
      .append("<style>\n")
      .append("body { font-family: Arial; font-size: 1em; }\n")
      .append("h2 { font-weight: normal; font-size: 1em; }\n")
      .append("h2, h2 a { color: #666; }\n")
      .append("h2 a:hover { color: #333; }\n")
      .append("img { border: 1px solid #ccc; }\n")
      .append("</style>\n")
      .append("</head><body>\n")
      .toString();
  }
 
  protected String getHTMLFooter() {
    return "</body></html>\n";
  }
 
  protected String getImageTag(IPath file) {
    return "<img src=\"" + file.lastSegment() + "\" usemap=\"#generated_map\">\n";
  }

  /**
   * Copied from DiagramGenerator.
   */
  private static final int DEFAULT_IMAGE_MARGIN_PIXELS = 10;
  private static final int DEFAULT_EMPTY_IMAGE_SIZE_PIXELS = 100;
 
  /**
   * Copied from DiagramGenerator.
   */
  private int getImageMargin(DiagramEditPart part) {
    return getMapMode(part).DPtoLP(DEFAULT_IMAGE_MARGIN_PIXELS);
  }

  /**
   * Copied from DiagramGenerator.
   */
  private Dimension getEmptyImageSize(DiagramEditPart part) {
    return (Dimension) getMapMode(part).DPtoLP(new Dimension(
        DEFAULT_EMPTY_IMAGE_SIZE_PIXELS,
        DEFAULT_EMPTY_IMAGE_SIZE_PIXELS));
  }

  /**
   * Copied from DiagramGenerator.
   */
  protected IMapMode getMapMode(DiagramEditPart part) {
    return MapModeUtil.getMapMode(part.getFigure());
  }

  /**
   * Extends the list of image formats to also include SVG.
   *
   * @see org.openiaml.model.diagram.custom.actions.ExportImagePartsAction#getExportedImageFormats()
   */
  @Override
  protected Set<ImageFileFormat> getExportedImageFormats() {
    Set<ImageFileFormat> set = new HashSet<ImageFileFormat>();
    set.addAll(super.getExportedImageFormats());
    set.add(ImageFileFormat.SVG);
    return set;
  }
 
 

}
TOP

Related Classes of org.openiaml.model.diagram.custom.actions.ExportToClickableHtml$EObjectDepthComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.