/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.foundation.gen;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import org.openflexo.foundation.FlexoModelObject;
import org.openflexo.foundation.FlexoXMLSerializableObject;
import org.openflexo.foundation.dm.ERDiagram;
import org.openflexo.foundation.ie.IEWOComponent;
import org.openflexo.foundation.ie.cl.ComponentDefinition;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.foundation.view.View;
import org.openflexo.foundation.view.ViewDefinition;
import org.openflexo.foundation.wkf.FlexoProcess;
import org.openflexo.foundation.wkf.FlexoWorkflow;
import org.openflexo.foundation.wkf.RoleList;
import org.openflexo.foundation.wkf.WKFObject;
import org.openflexo.foundation.wkf.WorkflowModelObject;
import org.openflexo.foundation.wkf.node.AbstractActivityNode;
import org.openflexo.foundation.wkf.node.LOOPOperator;
import org.openflexo.foundation.wkf.node.OperationNode;
import org.openflexo.logging.FlexoLogger;
import org.openflexo.module.ModuleLoadingException;
import org.openflexo.module.external.ExternalDMModule;
import org.openflexo.module.external.ExternalIEModule;
import org.openflexo.module.external.ExternalModule;
import org.openflexo.module.external.ExternalOEModule;
import org.openflexo.module.external.ExternalWKFModule;
import org.openflexo.swing.FlexoSwingUtils;
import org.openflexo.swing.ImageUtils;
import org.openflexo.toolbox.FileResource;
//TODO : rename it into ScreenshotBuilder
public class ScreenshotGenerator {
public static class ScreenshotImage {
public BufferedImage image;
public Rectangle trimInfo;
}
private static final Logger logger = FlexoLogger.getLogger(ScreenshotGenerator.class.getPackage().getName());
protected static final String BAD_FILE_NAME_CHARACTERS_REG_EXP = "[^-A-Za-z0-9]";
protected static final Pattern BAD_FILE_NAME_CHARACTERS_PATTERN = Pattern.compile(BAD_FILE_NAME_CHARACTERS_REG_EXP);
private static final String REPLACEMENT = "-";
public static String getScreenshotName(Object o) {
if (o instanceof FlexoProcess) {
return getImageNameForProcess((FlexoProcess) o);
} else if (o instanceof OperationNode) {
return getImageNameForOperation((OperationNode) o);
} else if (o instanceof AbstractActivityNode) {
return getImageNameForActivity((AbstractActivityNode) o);
} else if (o instanceof ComponentDefinition) {
return getImageNameForComponent((ComponentDefinition) o);
} else if (o instanceof IEWOComponent) {
return getImageNameForComponent(((IEWOComponent) o).getComponentDefinition());
} else if (o instanceof LOOPOperator) {
return getImageNameForLoopOperator((LOOPOperator) o);
} else if (o instanceof ERDiagram) {
return getImageNameForERDiagram((ERDiagram) o);
} else if (o instanceof RoleList) {
return getImageNameForRoleList((RoleList) o);
} else if (o instanceof FlexoWorkflow) {
return getImageNameForWorkflow((FlexoWorkflow) o);
} else if (o instanceof ViewDefinition) {
return getImageNameForShema((ViewDefinition) o);
} else if (o instanceof View) {
return getImageNameForShema(((View) o).getShemaDefinition());
}
return null;
}
private static String getImageNameForWorkflow(FlexoWorkflow o) {
return getImageName("WORKFLOW", o.getName(), o.getFlexoID());
}
private static String getImageNameForRoleList(RoleList o) {
return getImageName("ROLES", o.getWorkflow().getName(), o.getFlexoID());
}
/**
* @param definition
* @return
*/
private static String getImageNameForComponent(ComponentDefinition definition) {
return getImageName(IEWOComponent.getTypeName(), definition.getComponentName(), definition.getFlexoID());
}
/**
* @param node
* @return
*/
private static String getImageNameForActivity(AbstractActivityNode node) {
return getImageName(AbstractActivityNode.getTypeName(), node.getProcess().getName() + "-" + node.getName(), node.getFlexoID());
}
private static String getImageNameForLoopOperator(LOOPOperator node) {
return getImageName(LOOPOperator.getTypeName(), node.getProcess().getName() + "-" + node.getName(), node.getFlexoID());
}
private static String getImageNameForERDiagram(ERDiagram node) {
return getImageName("ERDiagram", node.getName(), node.getFlexoID());
}
/**
* @param node
* @return
*/
private static String getImageNameForOperation(OperationNode node) {
return getImageName(OperationNode.getTypeName(), node.getProcess().getName() + "-" + node.getName(), node.getFlexoID());
}
/**
* @param process
* @return
*/
private static String getImageNameForProcess(FlexoProcess process) {
return getImageName(FlexoProcess.getTypeName(), process.getName(), process.getFlexoID());
}
/**
* @param shema
* @return
*/
private static String getImageNameForShema(ViewDefinition shema) {
return getImageName(View.getTypeName(), shema.getName(), shema.getFlexoID());
}
private static String getImageName(String type, String name, long flexoID) {
return trim(formatImageName(type + "-" + name)) + flexoID;
}
private static String trim(String name) {
// Max-length is 255 chars
// We need to remove 20 characters for the flexoID Long.MAX_VALUE is 20 digits
// We need to remove 4 chars for the extension (.png)
// Let's be cautious and add an extra 30 chars security
if (name.length() > 200) {
return name.substring(0, 200).trim();
} else {
return name;
}
}
private static String formatImageName(String imageName) {
if (imageName == null) {
return null;
}
return BAD_FILE_NAME_CHARACTERS_PATTERN.matcher(imageName).replaceAll(REPLACEMENT);
}
public static ScreenshotImage getImage(FlexoModelObject object) {
if (object == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Object is null: cannot generate screenshot");
}
return getEmptyScreenshot();
}
logger.info("Generating screenshot for " + object + " of " + object.getClass().getSimpleName());
if (object.getXMLResourceData() instanceof FlexoXMLSerializableObject) {
((FlexoXMLSerializableObject) object.getXMLResourceData()).setIgnoreNotifications();
}
try {
ScreenshotComponentRunnable componentRunnable = new ScreenshotComponentRunnable(object);
JComponent component = null;
try {
component = FlexoSwingUtils.syncRunInEDT(componentRunnable);
} catch (Exception e1) {
e1.printStackTrace();
}
if (component == null) {
return getEmptyScreenshot();
}
ScreenshotImageRunnable runnable = new ScreenshotImageRunnable(component, object);
ScreenshotImage i = null;
try {
i = FlexoSwingUtils.syncRunInEDT(runnable);
FlexoSwingUtils.syncRunInEDT(new ScreenshotFinalizeRunnable(component, object));
} catch (Exception e) {
e.printStackTrace();
}
if (i == null) {
return getEmptyScreenshot();
}
return i;
} finally {
if (object.getXMLResourceData() instanceof FlexoXMLSerializableObject) {
((FlexoXMLSerializableObject) object.getXMLResourceData()).resetIgnoreNotifications();
}
}
}
private static class ScreenshotComponentRunnable implements Callable<JComponent> {
private FlexoModelObject object;
protected ScreenshotComponentRunnable(FlexoModelObject object) {
this.object = object;
}
@Override
public JComponent call() {
return getScreenshotComponent(object);
}
}
private static class ScreenshotImageRunnable implements Callable<ScreenshotImage> {
private FlexoModelObject object;
private JComponent component;
protected ScreenshotImageRunnable(JComponent component, FlexoModelObject object) {
this.component = component;
this.object = object;
}
@Override
public ScreenshotImage call() {
return createImageForComponent(component, object instanceof WKFObject || object instanceof WorkflowModelObject);
}
}
private static class ScreenshotFinalizeRunnable implements Callable<Void> {
private FlexoModelObject object;
private JComponent component;
protected ScreenshotFinalizeRunnable(JComponent component, FlexoModelObject object) {
this.component = component;
this.object = object;
}
@Override
public Void call() {
finalizeScreenshotGeneration(component, object);
return null;
}
}
private static JComponent getScreenshotComponent(FlexoModelObject object) {
ExternalWKFModule wkfModule = null;
ExternalIEModule ieModule = null;
ExternalDMModule dmModule = null;
ExternalOEModule oeModule = null;
FlexoProject project = object.getProject();
JComponent c = null;
try {
try {
if (object instanceof AbstractActivityNode || object instanceof FlexoProcess || object instanceof LOOPOperator
|| object instanceof RoleList || object instanceof FlexoWorkflow) {
wkfModule = project.getModuleLoader() != null ? project.getModuleLoader().getWKFModuleInstance() : null;
} else if (object instanceof IEWOComponent || object instanceof ComponentDefinition || object instanceof OperationNode) {
ieModule = project.getModuleLoader() != null ? project.getModuleLoader().getIEModuleInstance() : null;
} else if (object instanceof ERDiagram) {
dmModule = project.getModuleLoader() != null ? project.getModuleLoader().getDMModuleInstance() : null;
} else if (object instanceof View || object instanceof ViewDefinition) {
oeModule = project.getModuleLoader() != null ? project.getModuleLoader().getVEModuleInstance() : null;
}
} catch (ModuleLoadingException e) {
logger.warning("cannot load module (and so can't create screenshot)." + e.getMessage());
e.printStackTrace();
}
if (wkfModule != null || ieModule != null || dmModule != null || oeModule != null) {
ExternalModule module = ieModule != null ? ieModule : wkfModule != null ? wkfModule : dmModule != null ? dmModule
: oeModule;
synchronized (module) {// We synchronize on the module because modules cannot handle multiple screenshot generation
if (object instanceof FlexoProcess) {
c = wkfModule.createScreenshotForObject(object);
} else if (object instanceof AbstractActivityNode) {
c = wkfModule.createScreenshotForObject(object);
} else if (object instanceof LOOPOperator) {
c = wkfModule.createScreenshotForObject(object);
} else if (object instanceof RoleList) {
c = wkfModule.createScreenshotForObject(object);
} else if (object instanceof FlexoWorkflow) {
c = wkfModule.createScreenshotForObject(object);
} else if (object instanceof OperationNode) {
c = ieModule.createViewForOperation((OperationNode) object);
} else if (object instanceof ComponentDefinition) {
c = ieModule
.getWOComponentView(ieModule.getIEExternalController(), ((ComponentDefinition) object).getWOComponent());
} else if (object instanceof IEWOComponent) {
c = ieModule.getWOComponentView(ieModule.getIEExternalController(), ((IEWOComponent) object)
.getComponentDefinition().getWOComponent());
} else if (object instanceof ERDiagram) {
c = dmModule.createScreenshotForObject((ERDiagram) object);
} else if (object instanceof ViewDefinition) {
c = oeModule.createScreenshotForShema((ViewDefinition) object);
} else if (object instanceof View) {
c = oeModule.createScreenshotForShema(((View) object).getShemaDefinition());
}
return c;
}
} else {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("No module found to generate screenshot for object: " + object);
}
}
} catch (Throwable e) {
logger.severe("Failed to generate screenshot for " + object);
e.printStackTrace();
}
return c;
}
private static ScreenshotImage createImageForComponent(JComponent c, boolean trim) {
JFrame frame = new JFrame();
try {
BufferedImage bi = null;
c.setOpaque(true);
c.setBackground(Color.WHITE);
frame.setBackground(Color.WHITE);
frame.setUndecorated(true);
frame.getContentPane().add(c);
frame.pack();
bi = ImageUtils.createImageFromComponent(c);
ScreenshotImage i;
if (trim) {
i = trimImage(bi);
} else {
i = new ScreenshotImage();
i.image = bi;
i.trimInfo = new Rectangle(0, 0, bi.getWidth(), bi.getHeight());
}
return i;
} finally {
if (frame.getContentPane() != null) {
frame.getContentPane().removeAll();
}
frame.dispose();
}
}
private static void finalizeScreenshotGeneration(JComponent c, FlexoModelObject object) {
ExternalWKFModule wkfModule = null;
ExternalIEModule ieModule = null;
ExternalDMModule dmModule = null;
ExternalOEModule oeModule = null;
if (object.getXMLResourceData() instanceof FlexoXMLSerializableObject) {
((FlexoXMLSerializableObject) object.getXMLResourceData()).setIgnoreNotifications();
}
FlexoProject project = object.getProject();
try {
if (object instanceof AbstractActivityNode || object instanceof FlexoProcess || object instanceof LOOPOperator
|| object instanceof RoleList || object instanceof FlexoWorkflow) {
wkfModule = project.getModuleLoader() != null ? project.getModuleLoader().getWKFModuleInstance() : null;
} else if (object instanceof IEWOComponent || object instanceof ComponentDefinition || object instanceof OperationNode) {
ieModule = project.getModuleLoader() != null ? project.getModuleLoader().getIEModuleInstance() : null;
} else if (object instanceof ERDiagram) {
dmModule = project.getModuleLoader() != null ? project.getModuleLoader().getDMModuleInstance() : null;
} else if (object instanceof View || object instanceof ViewDefinition) {
oeModule = project.getModuleLoader() != null ? project.getModuleLoader().getVEModuleInstance() : null;
}
} catch (Throwable e) {
logger.severe("Failed to generate screenshot for " + object);
e.printStackTrace();
} finally {
if (wkfModule != null && c != null) {
wkfModule.finalizeScreenshotGeneration(c);
}
if (ieModule != null) {
ieModule.finalizeScreenshot();
}
if (dmModule != null) {
dmModule.finalizeScreenshot();
}
if (oeModule != null) {
oeModule.finalizeScreenshotGeneration();
}
}
}
public static ScreenshotImage makeImage(BufferedImage bi) {
ScreenshotImage i = new ScreenshotImage();
i.image = bi;
i.trimInfo = new Rectangle(0, 0, bi.getWidth(), bi.getHeight());
return i;
}
public static ScreenshotImage makeImage(BufferedImage bi, int left, int top, int width, int height) {
ScreenshotImage i = new ScreenshotImage();
i.image = bi.getSubimage(left, top, width, height);
i.trimInfo = new Rectangle(left, top, width, height);
return i;
}
public static ScreenshotImage trimImage(BufferedImage bi) {
// Trim operation to remove white borders.
int border = 10;
int top = -1;
int bottom = -1;
int left = -1;
int right = -1;
for (int i = bi.getWidth() - 1; i > 0 && right == -1; i--) {
for (int j = 0; j < bi.getHeight() && right == -1; j++) {
int color = bi.getRGB(i, j);
if (color != -1) {
right = i;
}
}
}
for (int i = 0; i < bi.getWidth() && left == -1; i++) {
for (int j = 0; j < bi.getHeight() && left == -1; j++) {
int color = bi.getRGB(i, j);
if (color != -1) {
left = i;
}
}
}
for (int j = 0; j < bi.getHeight() && top == -1; j++) {
for (int i = 0; i < bi.getWidth() && top == -1; i++) {
int color = bi.getRGB(i, j);
if (color != -1) {
top = j;
}
}
}
for (int j = bi.getHeight() - 1; j > 0 && bottom == -1; j--) {
for (int i = bi.getWidth() - 1; i > 0 && bottom == -1; i--) {
int color = bi.getRGB(i, j);
if (color != -1) {
bottom = j;
}
}
}
left = Math.max(left - border, 0);
top = Math.max(top - border, 0);
right = Math.min(right - left + border, bi.getWidth() - 1 - left);
bottom = Math.min(bottom - top + border, bi.getHeight() - 1 - top);
ScreenshotImage i = new ScreenshotImage();
i.image = bi.getSubimage(left, top, right, bottom);
i.trimInfo = new Rectangle(left, top, right, bottom);
return i;
}
/**
* @return
*/
private static ScreenshotImage getEmptyScreenshot() {
File f = new FileResource("LatexExtras/EmptyScreenshot.jpg");
if (f.exists()) {
try {
FileInputStream fis = new FileInputStream(f);
BufferedImage bi = ImageIO.read(fis);
ScreenshotImage i = new ScreenshotImage();
i.image = bi;
i.trimInfo = new Rectangle(0, 0, bi.getWidth(), bi.getHeight());
return i;
} catch (FileNotFoundException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("File " + f.getAbsolutePath() + " does not exist.");
}
} catch (IOException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Error trying to read file " + f.getAbsolutePath());
}
}
}
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("Cannot find " + f.getAbsolutePath() + " returning null");
}
return null;
}
}