Java tutorial
/** * Copyright 2007-2010 ? * All rights reserved. * * This library is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License v3 as published by the Free Software * Foundation. * * This program 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 Lesser General Public License along * with this library; if not, see http://www.gnu.org/licenses/lgpl.html. * */ package org.fireflow.webdesigner.transformer; import java.awt.Dimension; import java.io.InputStream; import java.io.StringWriter; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.fireflow.model.ModelElement; import org.fireflow.model.binding.ServiceBinding; import org.fireflow.model.io.SerializerException; import org.fireflow.model.servicedef.ServiceDef; import org.fireflow.pdl.fpdl.diagram.ActivityShape; import org.fireflow.pdl.fpdl.diagram.CommentShape; import org.fireflow.pdl.fpdl.diagram.Diagram; import org.fireflow.pdl.fpdl.diagram.DiagramElement; import org.fireflow.pdl.fpdl.diagram.EndNodeShape; import org.fireflow.pdl.fpdl.diagram.GroupShape; import org.fireflow.pdl.fpdl.diagram.LaneShape; import org.fireflow.pdl.fpdl.diagram.NodeShape; import org.fireflow.pdl.fpdl.diagram.PoolShape; import org.fireflow.pdl.fpdl.diagram.ProcessNodeShape; import org.fireflow.pdl.fpdl.diagram.RouterShape; import org.fireflow.pdl.fpdl.diagram.StartNodeShape; import org.fireflow.pdl.fpdl.diagram.figure.Figure; import org.fireflow.pdl.fpdl.diagram.figure.part.Bounds; import org.fireflow.pdl.fpdl.diagram.figure.part.BoundsImpl; import org.fireflow.pdl.fpdl.diagram.figure.part.Point; import org.fireflow.pdl.fpdl.io.FPDLNames; import org.fireflow.pdl.fpdl.process.Activity; import org.fireflow.pdl.fpdl.process.EndNode; import org.fireflow.pdl.fpdl.process.Router; import org.fireflow.pdl.fpdl.process.StartNode; import org.fireflow.pdl.fpdl.process.Synchronizer; import org.fireflow.pdl.fpdl.process.WorkflowProcess; import org.fireflow.pdl.fpdl.process.features.Feature; import org.fireflow.pdl.fpdl.process.features.endnode.NormalEndFeature; import org.fireflow.pdl.fpdl.process.features.endnode.ThrowCompensationFeature; import org.fireflow.pdl.fpdl.process.features.endnode.ThrowFaultFeature; import org.fireflow.pdl.fpdl.process.features.endnode.ThrowTerminationFeature; import org.fireflow.pdl.fpdl.process.features.router.impl.AndJoinAndSplitRouterFeature; import org.fireflow.pdl.fpdl.process.features.router.impl.CustomizedRouterFeature; import org.fireflow.pdl.fpdl.process.features.router.impl.DefaultRouterFeature; import org.fireflow.pdl.fpdl.process.features.router.impl.OrJoinOrSplitRouterFeature; import org.fireflow.pdl.fpdl.process.features.router.impl.XOrJoinXOrSplitRouterFeature; import org.fireflow.pdl.fpdl.process.features.startnode.CatchCompensationFeature; import org.fireflow.pdl.fpdl.process.features.startnode.CatchFaultFeature; import org.fireflow.pdl.fpdl.process.features.startnode.TimerStartFeature; import org.fireflow.pdl.fpdl.process.features.startnode.WebserviceStartFeature; import org.w3c.dom.Document; /** * * @author ? nychen2000@163.com * Fire Workflow www.firesoa.com www.fireflow.org * */ public abstract class AbstractFpdlDiagramSerializer implements FpdlDiagramSerializer { private Log log = LogFactory.getLog(AbstractFpdlDiagramSerializer.class); protected static DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); protected static final int IconTextGap = 5; protected static final int IMG_RADIUS = 16; protected static final int SVC_LOGO_RADIUS = 8; protected static final int DEFAULT_FONT_SIZE = 12; protected static final int GROUP_TITLE_HEIGHT = 20; protected static final int POOL_LANE_TITLE_HEIGHT = 32; protected static final String TYPE = "element_" + FPDLNames.TYPE; protected static final String ON_CLICK_HANDLER = "on_element_click"; protected static final String DEFAULT_DOT_DASHARRAY = "1 2"; protected static final String DEFAULT_DASH_DASHARRAY = "5 4"; protected static final String DEFAULT_DOTDASH_DASHARRAY = "2 4 5 4"; protected static final String DEFAULT_GROUP_DASHARRAY = "3 2"; protected static final String DEFAULT_ASSOC_DASHARRAY = DEFAULT_DASH_DASHARRAY; protected static final String DEFAULT_MESSAGEFLOW_DASHARRAY = DEFAULT_DASH_DASHARRAY; protected static final float PT_2_PX_RATE = 1.8f; protected static final Map<String, String> SERVICE_LOGO_CACHE = new HashMap<String, String>(); protected WorkflowProcess workflowProcess = null; protected String subProcessName = null; protected Document document = null; protected Diagram diagram = null; protected int leftTopX = 0;//x protected int leftTopY = 0;//y protected int rightDownX = 0;//?x protected int rightDownY = 0;//?y protected String resourcePathPrefix = null; protected void _refreshDiagramSize(Bounds bounds) { if (bounds.getX() < this.leftTopX) { this.leftTopX = bounds.getX(); } if (bounds.getY() < this.leftTopY) { this.leftTopY = bounds.getY(); } int x = bounds.getX() + bounds.getWidth(); int y = bounds.getY() + bounds.getHeight(); if (x > this.rightDownX) { this.rightDownX = x; } if (y > this.rightDownY) { this.rightDownY = y; } } protected void _refreshDiagramSize(List<Point> pointList) { if (pointList == null || pointList.size() == 0) { for (Point p : pointList) { if (p.getX() < this.leftTopX) { this.leftTopX = p.getX(); } if (p.getX() > this.rightDownX) { this.rightDownX = p.getX(); } if (p.getY() < this.leftTopY) { this.leftTopY = p.getY(); } if (p.getY() > this.rightDownY) { this.rightDownY = p.getY(); } } } } protected Point _calculateAnchor(NodeShape nodeShape, Bounds nodeBounds, Point refPoint) { Point boundsCenter = new Point(); boundsCenter.setX(nodeBounds.getX() + nodeBounds.getWidth() / 2); boundsCenter.setY(nodeBounds.getY() + nodeBounds.getHeight() / 2); if (nodeShape instanceof StartNodeShape || nodeShape instanceof EndNodeShape) { Point anchor = _calculateStartNodeEndNodeAnchor(nodeBounds, boundsCenter, refPoint); return anchor; } if (nodeShape instanceof ActivityShape || nodeShape instanceof CommentShape || nodeShape instanceof PoolShape || nodeShape instanceof LaneShape) { Point anchor = _calculateChopboxAnchor(nodeBounds, boundsCenter, refPoint); return anchor; } else if (nodeShape instanceof RouterShape) { Point anchor = _calculateRouterAnchor(nodeBounds, boundsCenter, refPoint); return anchor; } //bounds return boundsCenter; } protected Point _calculateRouterAnchor(Bounds r, Point center, Point reference) { float centerX = center.getX(); float centerY = center.getY(); if (r.getWidth() <= 0 || r.getHeight() <= 0 || (reference.getX() == (int) centerX && reference.getY() == (int) centerY)) return new Point((int) centerX, (int) centerY); // This avoids // divide-by-zero // Point ref = new Point(); ref.setX(reference.getX() - center.getX()); ref.setY(reference.getY() - center.getY()); if (Math.abs(ref.getX()) <= 1)//?1? return new Point(reference.getX(), (ref.getY() > 0) ? r.getY() + r.getHeight() : r.getY()); if (Math.abs(ref.getY()) <= 1)//?1? return new Point((ref.getX() > 0) ? r.getX() + r.getWidth() : r.getX(), reference.getY()); float dx = reference.getX() - centerX; float dy = reference.getY() - centerY; if (dy == 0) { if (dx > 0) {//right Point result = new Point(); result.setX(r.getX() + r.getWidth()); result.setY(r.getY() + r.getHeight() / 2); return result; } else {//left Point result = new Point(); result.setX(r.getX()); result.setY(r.getY() + r.getHeight() / 2); return result; } } if (dx == 0) { if (dy > 0) {//bottom Point result = new Point(); result.setX(r.getX() + r.getWidth() / 2); result.setY(r.getY() + r.getHeight()); return result; } else { Point result = new Point(); result.setX(r.getX() + r.getWidth() / 2); result.setY(r.getY()); return result; } } float scale = Math.abs(dx / dy); float ddy = 0.5f * r.getWidth() / (scale + 1); float ddx = Math.abs(scale * ddy); if (dx >= 0 && dy >= 0) { Point result = new Point(); result.setX((int) (centerX + ddx)); result.setY((int) (centerY + ddy)); return result; } else if (dx <= 0 && dy <= 0) { Point result = new Point(); result.setX((int) (centerX - ddx)); result.setY((int) (centerY - ddy)); return result; } else if (dx >= 0 && dy <= 0) { Point result = new Point(); result.setX((int) (centerX + ddx)); result.setY((int) (centerY - ddy)); return result; } else { Point result = new Point(); result.setX((int) (centerX - ddx)); result.setY((int) (centerY + ddy)); return result; } } protected Point _getLabelAbsolutePosition(Point fromAnchor, Point toAnchor, List<Point> linePoints, Point relativePos) { Point result = new Point(); Point centerP = null; if (linePoints != null && linePoints.size() > 0) { if ((linePoints.size() % 2) == 1) { int index = (linePoints.size() - 1) / 2; centerP = linePoints.get(index); } else { int index = (linePoints.size()) / 2; Point p1 = linePoints.get(index - 1); Point p2 = linePoints.get(index); centerP = new Point(); centerP.setX((p1.getX() + p2.getX()) / 2); centerP.setY((p1.getY() + p2.getY()) / 2); } } else { centerP = new Point(); centerP.setX((fromAnchor.getX() + toAnchor.getX()) / 2); centerP.setY((fromAnchor.getY() + toAnchor.getY()) / 2); } result.setX(centerP.getX() + relativePos.getX()); result.setY(centerP.getY() + relativePos.getY()); return result; } protected Point _calculateChopboxAnchor(Bounds r, Point center, Point reference) { r.setX(r.getX() - 1); r.setY(r.getY() - 1); r.setWidth(r.getWidth() + 1); r.setHeight(r.getHeight() + 1); float centerX = center.getX(); float centerY = center.getY(); if (r.getWidth() <= 0 || r.getHeight() <= 0 || (reference.getX() == (int) centerX && reference.getY() == (int) centerY)) return new Point((int) centerX, (int) centerY); // This avoids // Point ref = new Point(); ref.setX(reference.getX() - center.getX()); ref.setY(reference.getY() - center.getY()); if (Math.abs(ref.getX()) <= 1)//?1? return new Point(reference.getX(), (ref.getY() > 0) ? r.getY() + r.getHeight() : r.getY()); if (Math.abs(ref.getY()) <= 1)//?1? return new Point((ref.getX() > 0) ? r.getX() + r.getWidth() : r.getX(), reference.getY()); // divide-by-zero float dx = reference.getX() - centerX; float dy = reference.getY() - centerY; // r.width, r.height, dx, and dy are guaranteed to be non-zero. float scale = 0.5f / Math.max(Math.abs(dx) / r.getWidth(), Math.abs(dy) / r.getHeight()); dx *= scale; dy *= scale; centerX += dx; centerY += dy; return new Point(Math.round(centerX), Math.round(centerY)); } protected Point _calculateStartNodeEndNodeAnchor(Bounds r, Point center, Point reference) { Point ref = new Point(); ref.setX(reference.getX() - center.getX()); ref.setY(reference.getY() - center.getY()); //Point ref = r.getCenter().negate().translate(reference); if (Math.abs(ref.getX()) <= 1)//?1? return new Point(reference.getX(), (ref.getY() > 0) ? r.getY() + r.getHeight() : r.getY()); if (Math.abs(ref.getY()) <= 1)//?1? return new Point((ref.getX() > 0) ? r.getX() + r.getWidth() : r.getX(), reference.getY()); float dx = (ref.getX() > 0) ? 0.5f : -0.5f; float dy = (ref.getY() > 0) ? 0.5f : -0.5f; // ref.x, ref.y, r.width, r.height != 0 => safe to proceed float k = (float) (ref.getY() * r.getWidth()) / (ref.getX() * r.getHeight()); k = k * k; int ddx = (int) (r.getWidth() * dx / Math.sqrt(1 + k)); int ddy = (int) (r.getHeight() * dy / Math.sqrt(1 + 1 / k)); Point result = new Point(); result.setX(center.getX() + ddx); result.setY(center.getY() + ddy); return result; } protected Bounds _getAbsoluteBounds(DiagramElement diagramElm) { //TODO Activity? //TODO ??? Bounds result = null; Bounds original = null; if (diagramElm instanceof LaneShape) { LaneShape argLane = (LaneShape) diagramElm; original = new BoundsImpl(); original.setX(0); original.setY(0); original.setWidth(0); original.setHeight(0); PoolShape poolShape = (PoolShape) diagramElm.getParent(); List<DiagramElement> allLanes = poolShape.getChildren(); for (DiagramElement tmpElm : allLanes) { LaneShape lane = (LaneShape) tmpElm; Bounds tmpBounds = lane.getFigure().getBounds(); if (argLane.equals(lane)) { original.setWidth(tmpBounds.getWidth() - tmpBounds.getThick() * 2); original.setHeight(tmpBounds.getHeight() - tmpBounds.getThick() * 2); break; } else { if (Diagram.VERTICAL.equals(diagram.getDirection())) { original.setX(original.getX() + tmpBounds.getWidth()); } else { original.setY(original.getY() + tmpBounds.getHeight()); } } } result = original; } else if (diagramElm instanceof StartNodeShape || diagramElm instanceof EndNodeShape || diagramElm instanceof RouterShape) { original = diagramElm.getFigure() == null ? null : diagramElm.getFigure().getBounds(); if (original == null) { log.warn("NodeShape[id=" + diagramElm.getId() + "]bounds?bounds?????"); return null; } result = original.copy(); } else { original = diagramElm.getFigure() == null ? null : diagramElm.getFigure().getBounds(); if (original == null) { log.warn("NodeShape[id=" + diagramElm.getId() + "]bounds?bounds?????"); return null; } result = original.copy(); result.setX(result.getX() + result.getThick()); result.setY(result.getY() + result.getThick()); result.setWidth(result.getWidth() - result.getThick() * 2); result.setHeight(result.getHeight() - result.getThick() * 2); } DiagramElement parent = diagramElm.getParent(); if (parent == null) { return result; } else { Bounds parentBounds = _getAbsoluteBounds(parent); if (parentBounds != null) { if (parent instanceof GroupShape) { result.setX(parentBounds.getX() + result.getX()); result.setY( parentBounds.getY() + result.getY() + (GROUP_TITLE_HEIGHT - parentBounds.getThick())); } else if ((parent instanceof LaneShape) || (parent instanceof PoolShape)) { if (Diagram.VERTICAL.equals(diagram.getDirection())) { result.setX(parentBounds.getX() + result.getX()); result.setY(parentBounds.getY() + result.getY() + (POOL_LANE_TITLE_HEIGHT - parentBounds.getThick())); } else { result.setX(parentBounds.getX() + result.getX() + (POOL_LANE_TITLE_HEIGHT - parentBounds.getThick())); result.setY(parentBounds.getY() + result.getY()); } } else if (parent instanceof ActivityShape) {// ActivityShape actShape = (ActivityShape) parent; Bounds actBounds = actShape.getFigure().getBounds(); result.setX((parentBounds.getX() - parentBounds.getThick()) + result.getX()); result.setY(parentBounds.getY() + (actBounds.getHeight() - parentBounds.getThick() - IMG_RADIUS) + result.getY()); } else { result.setX(parentBounds.getX() + result.getX()); result.setY(parentBounds.getY() + result.getY()); } } } return result; } protected String _getSynchronizerNodeImgUri(ProcessNodeShape nodeShape) { ModelElement modelElm = nodeShape.getWorkflowElementRef(); Synchronizer node = (Synchronizer) modelElm; Feature f = node.getFeature(); String uri = null; // if (node instanceof StartNode) { if (f == null) { uri = "/org/fireflow/webdesigner/resources/images/obj32/empty_start_event.png"; } else if (f instanceof TimerStartFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/timer_start_event.png"; } else if (f instanceof WebserviceStartFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/message_start_event.png"; } else if ((f instanceof CatchFaultFeature)) { uri = "/org/fireflow/webdesigner/resources/images/obj32/error_start_event.png"; } else if (f instanceof CatchCompensationFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/compensation_start_event.png"; } else { uri = "/org/fireflow/webdesigner/resources/images/obj32/empty_start_event.png"; } } else if (node instanceof EndNode) { if (f == null) { uri = "/org/fireflow/webdesigner/resources/images/obj32/end_none_event.png"; } else if (f instanceof NormalEndFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/end_none_event.png"; } else if (f instanceof ThrowFaultFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/end_error_event.png"; } else if (f instanceof ThrowCompensationFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/end_compensation_event.png"; } else if (f instanceof ThrowTerminationFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/end_terminate_event.png"; } else { uri = "/org/fireflow/webdesigner/resources/images/obj32/end_none_event.png"; } } else if (node instanceof Router) { if (f == null) { uri = "/org/fireflow/webdesigner/resources/images/obj32/empty_gateway.png"; } else if (f instanceof AndJoinAndSplitRouterFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/parallel_gateway.png"; } else if (f instanceof OrJoinOrSplitRouterFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/inclusive_gateway.png"; } else if (f instanceof XOrJoinXOrSplitRouterFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/exclusive_gateway_2.jpg"; } else if (f instanceof CustomizedRouterFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/complex_gateway.png"; } else if (f instanceof DefaultRouterFeature) { uri = "/org/fireflow/webdesigner/resources/images/obj32/empty_gateway.png"; } else { uri = "/org/fireflow/webdesigner/resources/images/obj32/empty_gateway.png"; } } return uri; } /** * fontSize??pt * @param text * @param fontSize * @return */ protected Dimension _calculateFontSize(String text, int fontSize) { if (text == null || text.equals("")) { return new Dimension(0, 0); } else { int length = text.length(); return new Dimension((int) (fontSize * PT_2_PX_RATE * length), (int) (fontSize * PT_2_PX_RATE)); } } /** * ?diagram element<group>?view port * @param diagramElm * @return */ protected Bounds _getViewPortBounds(DiagramElement diagramElm) { Figure figure = diagramElm.getFigure(); ModelElement wfElm = diagramElm.getWorkflowElementRef(); int fontSize = DEFAULT_FONT_SIZE; if (figure.getTitleLabel() != null) { fontSize = figure.getTitleLabel().getFontSize(); } if (wfElm instanceof StartNode || wfElm instanceof EndNode || wfElm instanceof Router) { String displayName = wfElm.getDisplayName(); /* * displayName???namedisplayname??name if(displayName==null || displayName.trim().equals("")){ displayName = nodeWrapper.getName(); } */ // bounds Dimension dimension = null; dimension = _calculateFontSize(displayName, fontSize); dimension.width = dimension.width + 4;//?? Bounds bounds = figure.getBounds().copy(); if (dimension.width > 0) { // bounds.setHeight(bounds.getHeight() + IconTextGap + dimension.height); if (dimension.width > bounds.getWidth()) { int oldWidth = bounds.getWidth(); bounds.setWidth(dimension.width); int newX = bounds.getX() - ((dimension.width - oldWidth) / 2); bounds.setX(newX); } } return bounds; } else if (wfElm instanceof Activity) { Activity activity = (Activity) wfElm; //Activitybounds?Activity Bounds bounds = figure.getBounds().copy(); bounds.setHeight(bounds.getHeight() + IMG_RADIUS + DEFAULT_FONT_SIZE + IconTextGap); return bounds; } else { Bounds bounds = figure.getBounds().copy(); return bounds; } } protected void _init(WorkflowProcess workflowProcess, String subProcessName) throws SerializerException { this.workflowProcess = workflowProcess; this.subProcessName = subProcessName; try { String subProcessId = this.workflowProcess.getId() + WorkflowProcess.ID_SEPARATOR + this.subProcessName; _checkDiagram(this.workflowProcess, this.subProcessName); diagram = this.workflowProcess.getDiagramBySubProcessId(subProcessId); DocumentBuilder docBuilder = factory.newDocumentBuilder(); document = docBuilder.newDocument(); } catch (ParserConfigurationException e) { throw new SerializerException(e); } } protected String _makePointsSeq(Point fromAnchor, Point toAnchor, List<Point> pointList) { //linePointsfromAnchortoAnchor??? if (Math.abs(fromAnchor.getX() - toAnchor.getX()) <= 1) { fromAnchor.setX(toAnchor.getX()); } if (Math.abs(fromAnchor.getY() - toAnchor.getY()) <= 1) { fromAnchor.setY(toAnchor.getY()); } StringBuffer pointSeqBuf = new StringBuffer(); pointSeqBuf.append(fromAnchor.getX()).append(",").append(fromAnchor.getY()).append(" "); if (pointList != null && pointList.size() > 0) { for (Point p : pointList) { pointSeqBuf.append(p.getX()).append(",").append(p.getY()).append(" "); } } pointSeqBuf.append(toAnchor.getX()).append(",").append(toAnchor.getY()); return pointSeqBuf.toString(); } /** * ??diagram??diagram * * @param process * @param subProcessName */ protected void _checkDiagram(WorkflowProcess process, String subProcessId) { // TODO } protected String _getActivityImgUri(Activity activity) { ServiceBinding serviceBinding = activity.getServiceBinding(); if (serviceBinding == null) return null; String serviceId = serviceBinding.getServiceId(); ServiceDef serviceDef = this.workflowProcess.getService(serviceId); if (serviceDef == null) { return null; } String svcClassName = serviceDef.getClass().getName(); String pngUri = SERVICE_LOGO_CACHE.get(svcClassName); if (pngUri == null) { pngUri = "/" + svcClassName.replaceAll("\\.", "/") + ".png"; InputStream inStream = this.getClass().getResourceAsStream(pngUri); if (inStream != null) { SERVICE_LOGO_CACHE.put(svcClassName, pngUri); return pngUri; } else { SERVICE_LOGO_CACHE.put(svcClassName, "_LOGO_NOT_FOUND_"); return null; } } else if (pngUri.equals("_LOGO_NOT_FOUND_")) { return null; } else { return pngUri; } } public void setResourcePathPrefix(String s) { this.resourcePathPrefix = s; } public String serializeDiagramToStr(WorkflowProcess workflowProcess, String subProcessName, String encoding, boolean omitXmlDeclaration) { try { Document doc = this.serializeDiagramToDoc(workflowProcess, subProcessName); Transformer tf = TransformerFactory.newInstance().newTransformer(); if (omitXmlDeclaration) { tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); } tf.setOutputProperty(OutputKeys.ENCODING, encoding); tf.setOutputProperty(OutputKeys.INDENT, "yes"); tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); StreamResult dest = new StreamResult(new StringWriter()); tf.transform(new DOMSource(doc), dest); return dest.getWriter().toString(); } catch (Exception e) { //TODO e.printStackTrace(); } return ""; } }