/*
* Copyright (C) 2004 The Concord Consortium, Inc.,
* 10 Concord Crossing, Concord, MA 01742
*
* Web Site: http://www.concord.org
* Email: info@concord.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* END LICENSE */
package org.concord.otrunk;
import java.awt.Dimension;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import org.concord.framework.otrunk.OTControllerService;
import org.concord.framework.otrunk.OTID;
import org.concord.framework.otrunk.OTObject;
import org.concord.framework.otrunk.OTObjectService;
import org.concord.framework.otrunk.OTUser;
import org.concord.framework.otrunk.OTrunk;
import org.concord.framework.otrunk.view.OTControllerServiceFactory;
import org.concord.framework.otrunk.view.OTJComponentService;
import org.concord.framework.otrunk.view.OTJComponentServiceFactory;
import org.concord.framework.otrunk.view.OTJComponentView;
import org.concord.framework.otrunk.view.OTPrintDimension;
import org.concord.framework.otrunk.view.OTView;
import org.concord.framework.otrunk.view.OTViewContainer;
import org.concord.framework.otrunk.view.OTViewContext;
import org.concord.framework.otrunk.view.OTViewEntry;
import org.concord.framework.otrunk.view.OTViewFactory;
import org.concord.framework.otrunk.view.OTXHTMLHelper;
import org.concord.framework.otrunk.view.OTXHTMLView;
import org.concord.otrunk.util.ComponentImageSaver;
import org.concord.otrunk.view.OTConfig;
public class OTMLToXHTMLConverter
implements Runnable, OTXHTMLHelper
{
public final static String XHTML_EXPORT_WRAP_PROP = "otrunk.xhtml_export_wrap";
private OTObject[] topLevelOTObjects;
private OTViewEntry [] topLevelViewEntries;
private OTViewContainer viewContainer;
private OTViewFactory viewFactory;
private int containerDisplayWidth;
private int containerDisplayHeight;
private File outputFile;
OTJComponentService jComponentService;
private OTControllerService controllerService;
public OTMLToXHTMLConverter()
{
}
public OTMLToXHTMLConverter(OTViewFactory viewFactory,
OTViewContainer viewContainer)
{
setViewContainer(viewContainer);
setViewFactory(viewFactory, null);
}
public OTMLToXHTMLConverter(OTViewFactory viewFactory, OTObject otObject)
{
this(viewFactory, otObject, null);
}
public OTMLToXHTMLConverter(OTViewFactory viewFactory, OTObject otObject,
String mode)
{
this(viewFactory, otObject, null, mode);
}
public OTMLToXHTMLConverter(OTViewFactory viewFactory, OTObject otObject,
OTViewEntry viewEntry, String mode)
{
setViewFactory(viewFactory, mode);
this.topLevelOTObjects = new OTObject[1];
this.topLevelViewEntries = new OTViewEntry[1];
this.topLevelOTObjects[0] = otObject;
this.topLevelViewEntries[0] = viewEntry;
}
public OTMLToXHTMLConverter(OTViewFactory viewFactory, OTObject[] otObjects)
{
this(viewFactory, otObjects, OTViewFactory.NO_VIEW_MODE);
}
public OTMLToXHTMLConverter(OTViewFactory viewFactory, OTObject[] otObjects,
String mode)
{
setViewFactory(viewFactory, mode);
this.topLevelOTObjects = otObjects;
this.topLevelViewEntries = new OTViewEntry[otObjects.length];
}
public void setViewContainer(OTViewContainer viewContainer)
{
this.viewContainer = viewContainer;
if (viewContainer.getCurrentObject() != null) {
this.topLevelOTObjects = new OTObject[1];
this.topLevelOTObjects[0] = viewContainer.getCurrentObject();
this.topLevelViewEntries = new OTViewEntry[1];
this.topLevelViewEntries[0] = viewContainer.getCurrentViewEntry();
}
}
public void setViewFactory(OTViewFactory viewFactory, String mode)
{
this.viewFactory = viewFactory.getViewContext().createChildViewFactory();
this.viewFactory.setDefaultViewMode(mode);
this.viewFactory.getViewContext().setProperty(OTXHTMLView.ALWAYS_EMBED_XHTML_VIEW,
"true");
}
// This is not applicable.
// public void setPfDocument(PfDocument pfDocument) {
// this.pfDocument = pfDocument;
// }
public void setXHTMLParams(File file, int pageWidth, int pageHeight)
{
outputFile = file;
containerDisplayWidth = pageWidth;
containerDisplayHeight = pageHeight;
}
public void run()
{
boolean xhtmlOutput = OTConfig.getBooleanProp(XHTML_EXPORT_WRAP_PROP, true);
if (outputFile == null) {
return;
}
// FIXME: only the first object is used here it is not clear how to
// handle this. It is possible a collection of objects could be converted
// with different object services, so they should have different controller
// services.
if(topLevelOTObjects[0] != null){
controllerService = topLevelOTObjects[0].getOTObjectService().createControllerService();
OTControllerServiceFactory controllerServiceFactory = new OTControllerServiceFactory(){
public OTControllerService createControllerService(OTObjectService objectService)
{
OTControllerService subControllerService =
((OTControllerServiceImpl) controllerService).createSubControllerService(objectService);
return subControllerService;
}
/**
* @deprecated the object service should be passed in otherwise applications which
* use multiple overlay databases will not function properly
*
* @see org.concord.framework.otrunk.view.OTControllerServiceFactory#createControllerService()
*/
public OTControllerService createControllerService()
{
OTObjectService objectService =
((OTControllerServiceImpl) controllerService).getObjectService();
return createControllerService(objectService);
}
};
OTViewContext factoryContext = viewFactory.getViewContext();
factoryContext.addViewService(OTControllerServiceFactory.class, controllerServiceFactory);
}
String allTexts = "";
for (int i = 0; i < topLevelOTObjects.length; i++) {
if (topLevelOTObjects[i] == null) {
continue;
}
String text = embedOTObject(topLevelOTObjects[i], topLevelViewEntries[i], xhtmlOutput);
allTexts = allTexts + text;
}
try {
FileWriter fos = new FileWriter(outputFile);
if(xhtmlOutput){
fos.write("<html><body>" + allTexts + "</body></html>");
} else {
fos.write(allTexts);
}
fos.close();
} catch (FileNotFoundException exp) {
exp.printStackTrace();
} catch (IOException exp) {
exp.printStackTrace();
}
}
public String embedOTXHTMLView(OTObject topOTObject, OTViewEntry topViewEntry, OTXHTMLView xhtmlView, boolean xhtmlOutput)
{
String bodyText = xhtmlView.getXHTMLText(topOTObject);
Pattern p = Pattern.compile("<object refid=\"([^\"]*)\"([^>]*)>");
Matcher m = p.matcher(bodyText);
StringBuffer parsed = new StringBuffer();
OTObjectService objectService = topOTObject.getOTObjectService();
while (m.find()) {
String id = m.group(1);
OTID otid = objectService.getOTID(id);
OTObject referencedObject = null;
try {
referencedObject = objectService.getOTObject(otid);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Pattern userPat = Pattern.compile("user=\"([^\"]*)\"");
Matcher userMatcher = userPat.matcher(m.group(2));
if(userMatcher.find()){
String userId = userMatcher.group(1);
referencedObject = getRuntimeObject(referencedObject, userId);
}
OTViewEntry viewEntry = null;
Pattern viewPat = Pattern.compile("viewid=\"([^\"]*)\"");
Matcher viewMatcher = viewPat.matcher(m.group(2));
if(viewMatcher.find()){
String viewId = viewMatcher.group(1);
if(viewId != null && viewId.length() > 0) {
OTID viewOTid = objectService.getOTID(viewId);
try {
viewEntry = (OTViewEntry)
objectService.getOTObject(viewOTid);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// System.out.println(referencedObject.getClass());
String url = Matcher.quoteReplacement(embedOTObject(referencedObject, viewEntry, xhtmlOutput));
// String url = objView.getXHTMLText(folder,
// containerDisplayWidth, containerDisplayHeight);
if (url != null) {
try {
m.appendReplacement(parsed, url);
} catch (IllegalArgumentException e) {
System.err.println("bad replacement: " + url);
e.printStackTrace();
} catch (IndexOutOfBoundsException e) {
System.err.println("bad replacement (non-matching group reference): " + url);
e.printStackTrace();
}
}
}
m.appendTail(parsed);
if(xhtmlOutput){
return "<div>" + parsed.toString() + "</div><hr/>";
} else {
return parsed.toString();
}
}
/**
* This code attempts to save an image of the component. It does 3 things
* that are a bit odd but seem to make things work. 1. It calls addNotify on
* the component. This tricks it into thinking it has a parent, so it can be
* laid out. 2. It calls validate on the component that makes it get laid
* out.
*
* 3. The image saving code is placed into a invoke and wait call. Both
* setSize and validate cause events to be queued so we use Invoke and wait
* so these events get processed before we save the image by calling paint
* on it.
*
*/
public ComponentImageSaver saveComponentAsImage(JComponent comp, float scaleX, float scaleY,
OTObject otObject)
{
Dimension compSize = comp.getSize();
if (compSize.height <= 0 || compSize.width <= 0) {
throw new RuntimeException("Component size width: "
+ compSize.width + " height: " + compSize.height
+ " cannot be <=0");
}
comp.addNotify();
comp.validate();
String outputFileNameWithoutExtension = outputFile.getName().substring(
0, outputFile.getName().lastIndexOf('.'));
File folder = new File(outputFile.getParent(),
outputFileNameWithoutExtension + "_files");
if (!folder.exists())
folder.mkdir();
if (!folder.isDirectory())
return null;
ComponentImageSaver saver = new ComponentImageSaver(comp, folder, folder.getName(),
otObject, scaleX, scaleY, false);
try {
SwingUtilities.invokeAndWait(saver);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return saver;
}
public String embedComponent(JComponent comp, float scaleX, float scaleY,
OTObject otObject)
{
ComponentImageSaver saver = saveComponentAsImage(comp, scaleX, scaleY, otObject);
return saver.getText();
}
protected OTJComponentView getOTJComponentView(OTObject obj, OTViewEntry viewEntry)
{
return getOTJComponentView(obj, null, viewEntry);
}
protected OTJComponentView getOTJComponentView(OTObject obj, String mode, OTViewEntry viewEntry)
{
if (jComponentService == null) {
OTViewContext viewContext = viewFactory.getViewContext();
OTJComponentServiceFactory serviceFactory =
viewContext.getViewService(OTJComponentServiceFactory.class);
jComponentService = serviceFactory.createOTJComponentService(viewFactory, false);
}
if (viewEntry != null) {
return jComponentService.getObjectView(obj, viewContainer, mode, viewEntry);
} else {
return jComponentService.getObjectView(obj, viewContainer, mode);
}
}
protected JComponent getJComponent(OTObject obj, OTViewEntry viewEntry)
{
OTJComponentView objView = getOTJComponentView(obj, null, viewEntry);
JComponent comp = objView.getComponent(obj);
return comp;
}
public String embedOTObject(OTObject obj, OTViewEntry viewEntry, boolean xhtmlOutput)
{
OTView view = viewFactory.getView(obj, OTPrintDimension.class);
if (view == null) {
view = viewFactory.getView(obj, OTJComponentView.class);
}
if (view instanceof OTXHTMLView) {
return embedOTXHTMLView(obj, viewEntry, (OTXHTMLView)view, xhtmlOutput);
}
view = getOTJComponentView(obj, null, viewEntry);
if (view instanceof OTXHTMLView) {
return embedOTXHTMLView(obj, viewEntry, (OTXHTMLView)view, xhtmlOutput);
}
JComponent comp = ((OTJComponentView)view).getComponent(obj);
Dimension printDim = null;
if (view instanceof OTPrintDimension) {
OTPrintDimension dimView = (OTPrintDimension) view;
printDim = dimView.getPrintDimension(obj, containerDisplayWidth,
containerDisplayHeight);
}
if (printDim != null) {
comp.setSize(printDim);
} else {
Dimension dim2 = comp.getPreferredSize();
if (dim2.width == 0)
dim2.width = 1;
if (dim2.height == 0)
dim2.height = 1;
comp.setSize(dim2);
}
String url = getImgTagForComponent(obj, comp);
((OTJComponentView)view).viewClosed();
return url;
// return null;
}
private String getImgTagForComponent(OTObject obj, JComponent comp)
{
ComponentImageSaver saver = saveComponentAsImage(comp, 1, 1, obj);
String tag = "<img src='" + saver.getText() + "' " +
"width='" + saver.getWidth() + "' " +
"height='" + saver.getHeight() + "' " +
"/>";
return tag;
}
public OTObject getRuntimeObject(OTObject object, String userStr) {
try {
OTObjectService objectService = object.getOTObjectService();
OTrunk otrunk = objectService.getOTrunkService(OTrunk.class);
OTID userId = objectService.getOTID(userStr);
OTUser user = (OTUser) objectService.getOTObject(userId);
return otrunk.getUserRuntimeObject(object, user);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
public String embedOTObject(OTObject obj, OTViewEntry viewEntry)
{
return embedOTObject(obj, viewEntry, false);
}
}