/******************************************************************************* * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Tomas Milata - initial API and implementation ******************************************************************************/ package org.jboss.tools.batch.ui.editor.internal.services.diagram.layout.persistence; import static org.eclipse.sapphire.FileUtil.mkdirs; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.sapphire.ElementList; import org.eclipse.sapphire.Event; import org.eclipse.sapphire.FilteredListener; import org.eclipse.sapphire.Listener; import org.eclipse.sapphire.LoggingService; import org.eclipse.sapphire.Sapphire; import org.eclipse.sapphire.modeling.ByteArrayResourceStore; import org.eclipse.sapphire.modeling.ResourceStoreException; import org.eclipse.sapphire.modeling.ValidateEditException; import org.eclipse.sapphire.modeling.util.MiscUtil; import org.eclipse.sapphire.modeling.xml.RootXmlResource; import org.eclipse.sapphire.modeling.xml.XmlResourceStore; import org.eclipse.sapphire.ui.Point; import org.eclipse.sapphire.ui.diagram.ConnectionAddEvent; import org.eclipse.sapphire.ui.diagram.ConnectionBendpointsEvent; import org.eclipse.sapphire.ui.diagram.ConnectionDeleteEvent; import org.eclipse.sapphire.ui.diagram.ConnectionEvent; import org.eclipse.sapphire.ui.diagram.ConnectionLabelEvent; import org.eclipse.sapphire.ui.diagram.ConnectionService; import org.eclipse.sapphire.ui.diagram.DiagramConnectionPart; import org.eclipse.sapphire.ui.diagram.def.IDiagramNodeDef; import org.eclipse.sapphire.ui.diagram.editor.DiagramNodeAddEvent; import org.eclipse.sapphire.ui.diagram.editor.DiagramNodeBounds; import org.eclipse.sapphire.ui.diagram.editor.DiagramNodeDeleteEvent; import org.eclipse.sapphire.ui.diagram.editor.DiagramNodeEvent; import org.eclipse.sapphire.ui.diagram.editor.DiagramNodeMoveEvent; import org.eclipse.sapphire.ui.diagram.editor.DiagramNodePart; import org.eclipse.sapphire.ui.diagram.editor.DiagramNodeTemplate; import org.eclipse.sapphire.ui.diagram.editor.DiagramPageEvent; import org.eclipse.sapphire.ui.diagram.editor.SapphireDiagramEditorPagePart; import org.eclipse.sapphire.ui.diagram.editor.SapphireDiagramEditorPagePart.PostAutoLayoutEvent; import org.eclipse.sapphire.ui.diagram.editor.SapphireDiagramEditorPagePart.PreAutoLayoutEvent; //import org.eclipse.sapphire.ui.diagram.internal.StandardDiagramConnectionPart; //import org.eclipse.sapphire.ui.diagram.internal.StandardEmbeddedConnectionPart; import org.eclipse.sapphire.ui.diagram.layout.ConnectionHashKey; import org.eclipse.sapphire.ui.diagram.layout.DiagramLayoutPersistenceService; import org.eclipse.sapphire.ui.diagram.layout.standard.DiagramBendPointLayout; import org.eclipse.sapphire.ui.diagram.layout.standard.DiagramConnectionLayout; import org.eclipse.sapphire.ui.diagram.layout.standard.DiagramNodeLayout; import org.eclipse.sapphire.ui.diagram.layout.standard.StandardDiagramLayout; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IStorageEditorInput; import org.eclipse.ui.ide.FileStoreEditorInput; import org.eclipse.ui.part.FileEditorInput; import org.jboss.tools.batch.ui.BatchUIPlugin; import org.jboss.tools.batch.ui.editor.internal.model.Flow; import org.jboss.tools.batch.ui.editor.internal.model.FlowElement; import org.jboss.tools.batch.ui.editor.internal.model.Job; import org.jboss.tools.batch.ui.editor.internal.model.JobXMLEditor; import org.jboss.tools.common.util.FileUtil; /** * Saves layout to file in .metadata. One file contains all layouts for job and inner flow diagrams. * * @author Viacheslav Kabanovich * @author Tomas Milata */ @SuppressWarnings("restriction") public class BatchDiagramLayoutPersistenceService extends DiagramLayoutPersistenceService { // @Override // public DiagramConnectionInfo read(DiagramConnectionPart connection) { // return null; // } // // public boolean dirty() { // return true; // } protected StandardDiagramLayout layoutModel; protected IEditorInput editorInput; private Listener diagramEditorPagePartListener; private Listener connectionPartListener; private Map<String, DiagramNodeBounds> nodeBounds; private Map<ConnectionHashKey, List<Point>> connectionBendPoints; private Map<ConnectionHashKey, Point> connectionLabelPositions; private boolean dirty; private Map<ConnectionHashKey, DiagramConnectionPart> connectionIdMap; private Map<String, DiagramNodePart> nodeIdMap; private boolean autoLayout = false; @Override protected void init() { super.init(); this.editorInput = context( SapphireDiagramEditorPagePart.class ).getLocalModelElement().adapt(IEditorInput.class); this.nodeBounds = new HashMap<String, DiagramNodeBounds>(); this.connectionBendPoints = new HashMap<ConnectionHashKey, List<Point>>(); this.connectionLabelPositions = new HashMap<ConnectionHashKey, Point>(); this.dirty = false; this.connectionPartListener = new FilteredListener<ConnectionEvent>() { @Override protected void handleTypedEvent(ConnectionEvent event) { DiagramConnectionPart connPart = (DiagramConnectionPart)event.part(); if (event instanceof ConnectionLabelEvent) { if (((ConnectionLabelEvent)event).moveLabel()) { write(connPart); } } else if (event instanceof ConnectionBendpointsEvent) { ConnectionBendpointsEvent bpEvent = (ConnectionBendpointsEvent)event; if (bpEvent.reset()) { if (autoLayout) { addConnectionToPersistenceCache(event.part()); refreshDirtyState(); } else { write(event.part()); } } else { write(event.part()); } } } }; try { load(); refreshPersistedPartsCache(); } catch (Exception e) { Sapphire.service( LoggingService.class ).log(e); } addDiagramPartListener(); } @Override public void dispose() { if (diagramEditorPagePartListener != null) { context(SapphireDiagramEditorPagePart.class).detach(diagramEditorPagePartListener); } } protected StandardDiagramLayout initLayoutModel() { StandardDiagramLayout layoutModel = null; try { IEditorPart editor = BatchUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().findEditor(this.editorInput); String flow = null; Job job = (editor instanceof JobXMLEditor) ? ((JobXMLEditor)editor).getSchema() : null; if(editor instanceof JobXMLEditor && ((JobXMLEditor)editor).getCurrentDiagramModel() instanceof Flow) { flow = ((Flow)((JobXMLEditor)editor).getCurrentDiagramModel()).getId().content(); } else { flow = FileResourceStore2.ROOT; } File layoutFile = getLayoutPersistenceFile(); if (layoutFile != null) { final XmlResourceStore resourceStore = new XmlResourceStore( new FileResourceStore2(layoutFile, flow, job)); layoutModel = StandardDiagramLayout.TYPE.instantiate(new RootXmlResource( resourceStore )); } } catch (Exception e) { Sapphire.service(LoggingService.class ).log(e); } return layoutModel; } private static final String WORKSPACE_LAYOUT_FOLDER = ".metadata/.plugins/org.jboss.tools.batch.ui/layouts"; public File getLayoutPersistenceFile() throws IOException, CoreException { String fileName = computeLayoutFileName(this.editorInput); if (fileName != null) { return getLayoutPersistenceFile(fileName); } return null; } private File getLayoutPersistenceFile(String fileName) throws IOException { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); File layoutFolder = workspaceRoot.getLocation().toFile(); layoutFolder = new File(layoutFolder, WORKSPACE_LAYOUT_FOLDER); if (!layoutFolder.exists()) { mkdirs(layoutFolder); } File layoutFile = new File (layoutFolder, fileName); return layoutFile; } protected String computeLayoutFileName(IEditorInput editorInput) throws CoreException, IOException { // Compute a unique path for the layout file based on a hash associated with the editor input String uniquePath = null; if (editorInput instanceof FileEditorInput) { FileEditorInput fileEditorInput = (FileEditorInput)editorInput; IFile ifile = fileEditorInput.getFile(); uniquePath = ifile.getLocation().toPortableString(); } else if (editorInput instanceof FileStoreEditorInput) { FileStoreEditorInput fileStoreInput = (FileStoreEditorInput)editorInput; IFileStore store = EFS.getStore(fileStoreInput.getURI()); File localFile = store.toLocalFile(EFS.NONE, null); //if no local file is available, obtain a cached file if (localFile == null) localFile = store.toLocalFile(EFS.CACHE, null); if (localFile == null) throw new IllegalArgumentException(); uniquePath = localFile.getCanonicalPath(); } else if (editorInput instanceof IStorageEditorInput) { IStorageEditorInput storageEditorInput = (IStorageEditorInput) editorInput; IPath storagePath = storageEditorInput.getStorage().getFullPath(); if (storagePath != null) { uniquePath = storagePath.toPortableString(); } } return uniquePath != null ? MiscUtil.createStringDigest(uniquePath) : null; } public void load() throws ResourceStoreException, CoreException, IOException { layoutModel = initLayoutModel(); if (layoutModel == null) { return; } Boolean gridVisible = layoutModel.getGridLayout().isVisible().content(); Boolean showGuides = layoutModel.getGuidesLayout().isVisible().content(); // only set these if the layout file explicitly sets it. // If absent then fallback to diagram-editor-def setting if (gridVisible != null) { context(SapphireDiagramEditorPagePart.class).setGridVisible(gridVisible); } if (showGuides != null) { context( SapphireDiagramEditorPagePart.class ).setShowGuides(showGuides); } ElementList<DiagramNodeLayout> nodes = layoutModel.getDiagramNodesLayout(); ConnectionService connService = context(SapphireDiagramEditorPagePart.class).service(ConnectionService.class); for (DiagramNodeLayout node : nodes) { String nodeId = node.getNodeId().content(); DiagramNodePart nodePart = getNodePart(nodeId); int x = node.getX().content(); int y = node.getY().content(); int width = node.getWidth().content(); int height = node.getHeight().content(); if (nodePart != null) { nodePart.setNodeBounds(new DiagramNodeBounds(x, y, width, height, false, false)); } ElementList<DiagramConnectionLayout> connList = node.getEmbeddedConnectionsLayout(); for (DiagramConnectionLayout connLayout : connList) { String connId = connLayout.getConnectionId().content(); ElementList<DiagramBendPointLayout> bps = connLayout.getConnectionBendpoints(); DiagramConnectionPart connPart = getConnectionPart(connService, ConnectionHashKey.createKey(nodeId, connId)); if (connPart != null) { int index = 0; for (DiagramBendPointLayout pt : bps) { connPart.addBendpoint(index++, pt.getX().content(), pt.getY().content()); } if (connLayout.getLabelX().content(false) != null && connLayout.getLabelY().content(false) != null) { Point labelPos = new Point(connLayout.getLabelX().content(), connLayout.getLabelY().content()); connPart.setLabelPosition(labelPos); } } } } ElementList<DiagramConnectionLayout> connList = this.layoutModel.getDiagramConnectionsLayout(); for (DiagramConnectionLayout connLayout : connList) { String connId = connLayout.getConnectionId().content(); DiagramConnectionPart connPart = getConnectionPart(connService, ConnectionHashKey.createKey(null, connId)); ElementList<DiagramBendPointLayout> bps = connLayout.getConnectionBendpoints(); if (connPart != null) { int index = 0; for (DiagramBendPointLayout pt : bps) { connPart.addBendpoint(index++, pt.getX().content(), pt.getY().content()); } List<Point> bendPoints = new ArrayList<Point>(); bendPoints.addAll(connPart.getBendpoints()); if (connLayout.getLabelX().content(false) != null && connLayout.getLabelY().content(false) != null) { Point labelPos = new Point(connLayout.getLabelX().content(), connLayout.getLabelY().content()); connPart.setLabelPosition(labelPos); } } } // Listen on existing connection parts for (DiagramConnectionPart connPart : connService.list()) { connPart.attach(this.connectionPartListener); } } public void save() { if (layoutModel == null) { return; } addNodeBoundsToModel(); addConnectionsToModel(); refreshPersistedPartsCache(); try { layoutModel.resource().save(); } catch (ResourceStoreException rse) { Sapphire.service(LoggingService.class).log(rse); } // Clear the dirty state dirty = false; } private void read(DiagramNodePart nodePart) { String id = nodePart.getId(); if (this.nodeBounds.containsKey(id) && this.nodeBounds.get(id) != null) { nodePart.setNodeBounds(this.nodeBounds.get(id)); } } private void write(DiagramNodePart nodePart) { refreshDirtyState(); } public DiagramConnectionInfo read(DiagramConnectionPart connPart) { ConnectionHashKey key = ConnectionHashKey.createKey(connPart); if (this.connectionBendPoints.containsKey(key)) { DiagramConnectionInfo connectionInfo = new DiagramConnectionInfo(this.connectionBendPoints.get(key),this.connectionLabelPositions.get(key)); return connectionInfo; } else { return null; } } private void write(DiagramConnectionPart connPart) { refreshDirtyState(); } private void setGridVisible(boolean visible) { if (this.layoutModel != null) { this.layoutModel.getGridLayout().setVisible(visible); } } private void setGuidesVisible(boolean visible) { if (this.layoutModel != null) { this.layoutModel.getGuidesLayout().setVisible(visible); } } private void addNodeBoundsToModel() { this.layoutModel.getDiagramNodesLayout().clear(); for (DiagramNodeTemplate nodeTemplate : context( SapphireDiagramEditorPagePart.class ).getNodeTemplates()) { for (DiagramNodePart nodePart : nodeTemplate.getDiagramNodes()) { String nodeId = nodePart.getId(); DiagramNodeLayout diagramNode = this.layoutModel.getDiagramNodesLayout().insert(); diagramNode.setNodeId(nodeId); DiagramNodeBounds bounds = nodePart.getNodeBounds(); diagramNode.setX(bounds.getX()); diagramNode.setY(bounds.getY()); if (nodePart.canResizeShape()) { IDiagramNodeDef nodeDef = (IDiagramNodeDef)nodePart.definition(); if (bounds.getHeight() != -1 && ((nodeDef.getHeight().content() != null && nodeDef.getHeight().content() != bounds.getHeight()) || nodeDef.getHeight().content() == null)) { diagramNode.setHeight(bounds.getHeight()); } if (bounds.getWidth() != -1 && ((nodeDef.getWidth().content() != null && nodeDef.getWidth().content() != bounds.getWidth()) || nodeDef.getWidth().content() == null)) { diagramNode.setWidth(bounds.getWidth()); } } // save the embedded connection bendpoints diagramNode.getEmbeddedConnectionsLayout().clear(); if (nodePart.getDiagramNodeTemplate().getEmbeddedConnectionTemplate() != null) { List</*Standard*/DiagramConnectionPart> connParts = getEmbedded(nodePart); for (/*Standard*/DiagramConnectionPart connPart : connParts) { String connId = connPart.getId(); DiagramConnectionLayout conn = null; List<Point> connBendPoints = connPart.getBendpoints(); if (connBendPoints.size() > 0) { conn = diagramNode.getEmbeddedConnectionsLayout().insert(); conn.setConnectionId(connId); for (Point pt : connBendPoints) { DiagramBendPointLayout pt2 = conn.getConnectionBendpoints().insert(); pt2.setX(pt.getX()); pt2.setY(pt.getY()); } } if (connPart.getLabel() != null && connPart.getLabelPosition() != null) { if (conn == null) { conn = diagramNode.getEmbeddedConnectionsLayout().insert(); conn.setConnectionId(connId); } conn.setLabelX(connPart.getLabelPosition().getX()); conn.setLabelY(connPart.getLabelPosition().getY()); } } } } } } private List<DiagramConnectionPart> getEmbedded(DiagramNodePart nodePart) { // Restricted! // List<StandardDiagramConnectionPart> connParts = // nodePart.getDiagramNodeTemplate().getEmbeddedConnectionTemplate().getDiagramConnections(nodeElement); List</*Standard*/DiagramConnectionPart> connParts = new ArrayList<DiagramConnectionPart>(); ConnectionService connService = context(SapphireDiagramEditorPagePart.class).service(ConnectionService.class); List<DiagramConnectionPart> list = connService.list(); for (DiagramConnectionPart c: list) { if(c.getEndpoint1() == nodePart.getLocalModelElement() && isEmbedded(c)) { connParts.add(c); } } return connParts; } private boolean isEmbedded(DiagramConnectionPart c) { /*(connPart instanceof StandardEmbeddedConnectionPart)*/ return c != null && "org.eclipse.sapphire.ui.diagram.internal.StandardEmbeddedConnectionPart".equals(c.getClass().getName()); } private void addConnectionsToModel() { layoutModel.getDiagramConnectionsLayout().clear(); ConnectionService connService = context(SapphireDiagramEditorPagePart.class).service(ConnectionService.class); for (DiagramConnectionPart connPart : connService.list()) { if (!connPart.removable() || isEmbedded(connPart)) continue; String id = connPart.getId(); DiagramConnectionLayout conn = null; List<Point> connBendPoints = connPart.getBendpoints(); if (connBendPoints.size() > 0) { conn = layoutModel.getDiagramConnectionsLayout().insert(); conn.setConnectionId(id); for (Point pt : connBendPoints) { DiagramBendPointLayout pt2 = conn.getConnectionBendpoints().insert(); pt2.setX(pt.getX()); pt2.setY(pt.getY()); } } if (connPart.getLabel() != null && connPart.getLabelPosition() != null) { if (conn == null) { conn = this.layoutModel.getDiagramConnectionsLayout().insert(); conn.setConnectionId(id); } conn.setLabelX(connPart.getLabelPosition().getX()); conn.setLabelY(connPart.getLabelPosition().getY()); } } } private void addNodeToPersistenceCache(DiagramNodePart nodePart) { String nodeId = nodePart.getId(); nodeBounds.put(nodeId, nodePart.getNodeBounds()); } private void addConnectionToPersistenceCache(DiagramConnectionPart connPart) { ConnectionHashKey connKey = ConnectionHashKey.createKey(connPart); this.connectionBendPoints.put(connKey, connPart.getBendpoints()); if (connPart.getLabel() != null && connPart.getLabelPosition() != null) { connectionLabelPositions.put(connKey, new Point(connPart.getLabelPosition())); } } private void refreshPersistedPartsCache() { nodeBounds.clear(); connectionBendPoints.clear(); ConnectionService connService = context(SapphireDiagramEditorPagePart.class).service(ConnectionService.class); for (DiagramConnectionPart connPart : connService.list()) { if (connPart.removable()) { addConnectionToPersistenceCache(connPart); } } for (DiagramNodePart nodePart : context( SapphireDiagramEditorPagePart.class ).getNodes()) { addNodeToPersistenceCache(nodePart); } } private void addDiagramPartListener() { diagramEditorPagePartListener = new Listener() { @Override public void handle( final Event event ) { if ( event instanceof DiagramNodeEvent ) { handleDiagramNodeEvent((DiagramNodeEvent)event); } else if ( event instanceof DiagramPageEvent ) { handleDiagramPageEvent((DiagramPageEvent)event); } else if (event instanceof PreAutoLayoutEvent) { autoLayout = true; } else if (event instanceof PostAutoLayoutEvent) { autoLayout = false; } else if (event instanceof ConnectionAddEvent) { handleConnectionAddEvent((ConnectionAddEvent)event); } else if (event instanceof ConnectionDeleteEvent) { handleConnectionDeleteEvent((ConnectionDeleteEvent)event); } } }; context(SapphireDiagramEditorPagePart.class).attach(diagramEditorPagePartListener); } private void handleDiagramNodeEvent(DiagramNodeEvent event) { DiagramNodePart nodePart = (DiagramNodePart)event.part(); if (event instanceof DiagramNodeAddEvent) { read(nodePart); } else if (event instanceof DiagramNodeDeleteEvent) { refreshDirtyState(); } else if (event instanceof DiagramNodeMoveEvent) { DiagramNodeBounds nodeBounds = nodePart.getNodeBounds(); if (nodeBounds.isAutoLayout()) { // need to add the node bounds to the persistence cache so that "revert" could work addNodeToPersistenceCache(nodePart); refreshDirtyState(); } else if (!nodeBounds.isDefaultPosition()) { write((DiagramNodePart)event.part()); } } } protected void handleConnectionAddEvent(ConnectionAddEvent event) { DiagramConnectionPart connPart = event.part(); connPart.attach(this.connectionPartListener); DiagramConnectionInfo connInfo = read(connPart); if (connInfo != null) { connPart.resetBendpoints(connInfo.getBendPoints()); if (connInfo.getLabelPosition() != null) { connPart.setLabelPosition(connInfo.getLabelPosition()); } } } protected void handleConnectionDeleteEvent(ConnectionDeleteEvent event) { refreshDirtyState(); } private void handleDiagramPageEvent(DiagramPageEvent event) { SapphireDiagramEditorPagePart diagramPart = (SapphireDiagramEditorPagePart)event.part(); switch(event.getDiagramPageEventType()) { case GridStateChange: setGridVisible(diagramPart.isGridVisible()); break; case GuideStateChange: setGuidesVisible(diagramPart.isShowGuides()); break; case DiagramSave: save(); break; default: break; } } private boolean isNodeLayoutChanged(DiagramNodePart nodePart) { DiagramNodeBounds newBounds = nodePart.getNodeBounds(); boolean changed = false; String nodeId = nodePart.getId(); if (nodeBounds.containsKey(nodeId)) { DiagramNodeBounds oldBounds = this.nodeBounds.get(nodeId); if (!newBounds.equals(oldBounds)) { changed = true; } } else { changed = true; } return changed; } private boolean isConnectionLayoutChanged(DiagramConnectionPart connPart) { // Detect whether the connection bendpoints or connection label have been changed. List<Point> bendpoints = connPart.getBendpoints(); ConnectionHashKey key = ConnectionHashKey.createKey(connPart); boolean changed = false; if (this.connectionBendPoints.containsKey(key)) { List<Point> oldBendpoints = this.connectionBendPoints.get(key); if (bendpoints.size() != oldBendpoints.size()) { changed = true; } else { for (int i = 0; i < bendpoints.size(); i++) { Point newPt = bendpoints.get(i); Point oldPt = oldBendpoints.get(i); if (newPt.getX() != oldPt.getX() || newPt.getY() != oldPt.getY()) { changed = true; break; } } } if (!changed) { if (connPart.getLabel() != null) { Point newPos = connPart.getLabelPosition(); Point oldPos = this.connectionLabelPositions.get(key); if ((newPos == null && oldPos != null) || (newPos != null && oldPos == null) || (newPos != null && oldPos != null && !newPos.equals(oldPos))) { changed = true; } } } } else { changed = true; } return changed; } private boolean isDiagramLayoutChanged() { boolean changed = false; if (!context(SapphireDiagramEditorPagePart.class).disposed()) { for (DiagramNodePart nodePart : context( SapphireDiagramEditorPagePart.class ).getNodes()) { if (!nodePart.getLocalModelElement().disposed() && isNodeLayoutChanged(nodePart)) { changed = true; break; } } ConnectionService connService = context(SapphireDiagramEditorPagePart.class).service(ConnectionService.class); for (DiagramConnectionPart connPart : connService.list()) { if (!connPart.getLocalModelElement().disposed() && connPart.removable() && isConnectionLayoutChanged(connPart)) { changed = true; break; } } } return changed; } @Override public boolean dirty() { return dirty; } private void refreshDirtyState() { boolean after = isDiagramLayoutChanged(); if( this.dirty != after ) { boolean before = this.dirty; this.dirty = after; broadcast(new DirtyStateEvent(this, before, after)); } } private DiagramConnectionPart getConnectionPart(ConnectionService connService, ConnectionHashKey connId) { if (connectionIdMap == null) { connectionIdMap = new HashMap<ConnectionHashKey, DiagramConnectionPart>(); for (DiagramConnectionPart connPart : connService.list()) { connectionIdMap.put(ConnectionHashKey.createKey(connPart), connPart); } } return connectionIdMap.get(connId); } private DiagramNodePart getNodePart(String nodeId) { if (nodeIdMap == null) { nodeIdMap = new HashMap<String, DiagramNodePart>(); for (DiagramNodePart nodePart : context( SapphireDiagramEditorPagePart.class ).getNodes()) { nodeIdMap.put(nodePart.getId(), nodePart); } } return nodeIdMap.get(nodeId); } } class FileResourceStore2 extends ByteArrayResourceStore { static final String START = "{"; static final String NAME_END = "-"; static final String END = "}"; static final String ROOT = "Root"; private final File file; private String flowName; private Job job; public FileResourceStore2(final File file, String flowName, Job job) throws ResourceStoreException { this.file = file; this.flowName = flowName; this.job = job; if(this.file.exists() ) { String contents = FileUtil.readFile(file); int i = contents.indexOf(START + flowName + NAME_END); int j = (i < 0) ? -1 : contents.indexOf(END, i); if(j > 0) { String c = contents.substring(i + flowName.length() + 2, j); setContents(new ByteArrayInputStream(c.getBytes())); } } } public File getFile() { return this.file; } @Override public void save() throws ResourceStoreException { validateSave(); try { mkdirs( this.file.getParentFile() ); } catch( final IOException e ) { throw new ResourceStoreException( e ); } String contents = FileUtil.readFile(file); contents = cleanContents(contents); try( OutputStream out = new FileOutputStream( this.file ) ) { int i = contents.indexOf(START + flowName + NAME_END); int j = (i < 0) ? -1 : contents.indexOf(END, i); if(j > 0) { contents = contents.substring(0, i + flowName.length() + 2) + new String(getContents()) + contents.substring(j); } else { contents = contents + "\n" + START + flowName + NAME_END + new String(getContents()) + END + "\n"; } out.write(contents.getBytes()); out.flush(); } catch( final IOException e ) { throw new ResourceStoreException( e ); } } @Override public void validateSave() { if(this.file.exists()) { if(!this.file.canWrite()) { // TODO: Add conditional call to Java 6 specific setWritable API. //if( ! this.file.setWritable( true ) ) //{ // throw new ValidateEditException(); //} throw new ValidateEditException(); } } } @Override public <A> A adapt( final Class<A> adapterType ) { if(adapterType == File.class) { return adapterType.cast(this.file); } else { return super.adapt( adapterType ); } } String cleanContents(String contents) { Set<String> flows = collectFlowIds(job); StringBuffer result = new StringBuffer(); int i = 0; while(contents.indexOf(START, i) >= 0) { int i1 = contents.indexOf(START, i); int i2 = contents.indexOf(NAME_END, i1); int i3 = contents.indexOf(END, i1); if(i3 < 0) i3 = contents.length(); String flow = contents.substring(i1 + 1, i2); if(flows.contains(flow)) { result.append(contents.substring(i, i3 + 1)); } i = i3 + 1; } result.append(contents.substring(i)); return result.toString(); } Set<String> collectFlowIds(Job job) { Set<String> result = new HashSet<String>(); result.add(ROOT); collectFlowIds(job.getFlowElements(), result); return result; } void collectFlowIds(ElementList<FlowElement> es, Set<String> result) { for (FlowElement f: es) { if(f instanceof Flow) { result.add(f.getId().content()); collectFlowIds(((Flow) f).getFlowElements(), result); } } } }