/*******************************************************************************
* Copyright (c) 2012 VMware, Inc.
* All rights reserved. This program and the accompanying materials
* are 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:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.config.graph.model;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.wst.sse.ui.StructuredTextEditor;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
import org.springframework.ide.eclipse.beans.ui.editor.util.BeansEditorUtils;
import org.springframework.ide.eclipse.config.core.contentassist.SpringConfigContentAssistProcessor;
import org.springframework.ide.eclipse.config.core.extensions.PageAdaptersExtensionPointConstants;
import org.springframework.ide.eclipse.config.graph.AbstractConfigGraphicalEditor;
import org.springframework.ide.eclipse.config.graph.ConfigGraphPlugin;
import org.springframework.ide.eclipse.core.SpringCorePreferences;
import org.springsource.ide.eclipse.commons.core.StatusHandler;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author Leo Dos Santos
* @author Christian Dupuis
*/
@SuppressWarnings("restriction")
public abstract class AbstractConfigGraphDiagram extends ParallelActivity {
private static String KEY_COORDINATES = "coordinates"; //$NON-NLS-1$
private static String KEY_ELEMENT = "element"; //$NON-NLS-1$
private static String KEY_ELEMENT_TYPE = "type"; //$NON-NLS-1$
private static String KEY_STRUCTURE = "structure"; //$NON-NLS-1$
private static String KEY_STRUCTURE_START = "start"; //$NON-NLS-1$
private static String KEY_STRUCTURE_STARTEND = "startend"; //$NON-NLS-1$
private static String KEY_STRUCTURE_ENDSTART = "endstart"; //$NON-NLS-1$
private static String KEY_STRUCTURE_END = "end"; //$NON-NLS-1$
private static String KEY_BOUNDS = "bounds"; //$NON-NLS-1$
private static String KEY_BOUNDS_X = "x"; //$NON-NLS-1$
private static String KEY_BOUNDS_Y = "y"; //$NON-NLS-1$
private static String KEY_BOUNDS_HEIGHT = "height"; //$NON-NLS-1$
private static String KEY_BOUNDS_WIDTH = "width"; //$NON-NLS-1$
private static String KEY_MODEL_CLASS = "clazz"; //$NON-NLS-1$
private final AbstractConfigGraphicalEditor editor;
private final List<Activity> modelRegistry;
private Map<String, Node> refNodeRegistry;
public AbstractConfigGraphDiagram(AbstractConfigGraphicalEditor editor) {
super();
this.editor = editor;
modelRegistry = new ArrayList<Activity>();
refNodeRegistry = new HashMap<String, Node>();
}
@Override
protected void createInput(String uri) {
// no-op
}
public Rectangle doReadCoordinates(Activity activity) {
String xml = getPreferenceLocation().getString(getGraphCoordsKey(), ""); //$NON-NLS-1$
Rectangle savedBounds = null;
if (xml != null && xml.length() > 0) {
try {
IMemento memento = XMLMemento.createReadRoot(new StringReader(xml));
IMemento[] elementMementos = memento.getChildren(KEY_ELEMENT);
for (IMemento element : elementMementos) {
IDOMElement input = activity.getInput();
if (activity.getInputName().equals(element.getString(KEY_ELEMENT_TYPE))
&& activity.getClass().getSimpleName().equals(element.getString(KEY_MODEL_CLASS))) {
IMemento structureMemento = element.getChild(KEY_STRUCTURE);
if (structureMemento != null
&& structureMemento.getInteger(KEY_STRUCTURE_START).equals(input.getStartOffset())
&& structureMemento.getInteger(KEY_STRUCTURE_STARTEND)
.equals(input.getStartEndOffset())
&& structureMemento.getInteger(KEY_STRUCTURE_ENDSTART)
.equals(input.getEndStartOffset())
&& structureMemento.getInteger(KEY_STRUCTURE_END).equals(input.getEndOffset())) {
IMemento boundsMemento = element.getChild(KEY_BOUNDS);
savedBounds = new Rectangle(boundsMemento.getInteger(KEY_BOUNDS_X),
boundsMemento.getInteger(KEY_BOUNDS_Y), boundsMemento.getInteger(KEY_BOUNDS_WIDTH),
boundsMemento.getInteger(KEY_BOUNDS_HEIGHT));
}
}
}
}
catch (WorkbenchException e) {
StatusHandler.log(new Status(IStatus.ERROR, ConfigGraphPlugin.PLUGIN_ID,
Messages.AbstractConfigFlowDiagram_ERROR_READING_COORDINATES));
}
}
return savedBounds;
}
public void doResetCoordinates() {
getPreferenceLocation().putString(getGraphCoordsKey(), "");
}
public void doSaveCoordinates() {
String xml = null;
XMLMemento memento = XMLMemento.createWriteRoot("graph"); //$NON-NLS-1$
for (Activity child : children) {
IMemento childMemento = memento.createChild(KEY_ELEMENT);
childMemento.putString(KEY_ELEMENT_TYPE, child.getInputName());
childMemento.putString(KEY_MODEL_CLASS, child.getClass().getSimpleName());
IDOMElement input = child.getInput();
IMemento structureMemento = childMemento.createChild(KEY_STRUCTURE);
structureMemento.putInteger(KEY_STRUCTURE_START, input.getStartOffset());
structureMemento.putInteger(KEY_STRUCTURE_STARTEND, input.getStartEndOffset());
structureMemento.putInteger(KEY_STRUCTURE_ENDSTART, input.getEndStartOffset());
structureMemento.putInteger(KEY_STRUCTURE_END, input.getEndOffset());
Rectangle bounds = child.getBounds();
IMemento boundsMemento = childMemento.createChild(KEY_BOUNDS);
boundsMemento.putInteger(KEY_BOUNDS_X, bounds.x);
boundsMemento.putInteger(KEY_BOUNDS_Y, bounds.y);
boundsMemento.putInteger(KEY_BOUNDS_HEIGHT, bounds.height);
boundsMemento.putInteger(KEY_BOUNDS_WIDTH, bounds.width);
}
try {
StringWriter writer = new StringWriter();
memento.save(writer);
xml = writer.getBuffer().toString();
getPreferenceLocation().putString(getGraphCoordsKey(), xml);
}
catch (IOException e) {
StatusHandler.log(new Status(IStatus.ERROR, ConfigGraphPlugin.PLUGIN_ID,
Messages.AbstractConfigFlowDiagram_ERROR_SAVING_COORDINATES));
}
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class adapter) {
if (IPropertySource.class == adapter) {
return null;
}
return super.getAdapter(adapter);
}
@Override
protected List<Activity> getChildrenFromXml() {
List<Activity> list = super.getChildrenFromXml();
list.addAll(getNestedChildrenFromXml(getInput()));
return list;
}
@Override
public AbstractConfigGraphDiagram getDiagram() {
return this;
}
public IDOMDocument getDomDocument() {
return editor.getDomDocument();
}
private String getGraphCoordsKey() {
return KEY_COORDINATES + ":" + getNamespaceUri() + ":" + getResourceFile().getFullPath().toString(); //$NON-NLS-1$ //$NON-NLS-2$
}
public AbstractConfigGraphicalEditor getGraphicalEditor() {
return editor;
}
@Override
public IDOMElement getInput() {
return (IDOMElement) getDomDocument().getDocumentElement();
}
@Override
public String getInputName() {
return ""; //$NON-NLS-1$
}
protected abstract IDiagramModelFactory getModelFactory();
@Override
public List<Activity> getModelRegistry() {
return modelRegistry;
}
public String getNamespaceUri() {
return editor.getNamespaceUri();
}
private List<Activity> getNestedChildrenFromXml(IDOMElement parent) {
List<Activity> list = new ArrayList<Activity>();
NodeList children = parent.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child instanceof IDOMElement) {
IDOMElement childElem = (IDOMElement) child;
if (getNamespaceUri() != null && getNamespaceUri().equals(child.getNamespaceURI())) {
getModelFactory().getNestedChildrenFromXml(list, childElem, this);
}
else {
for (IConfigurationElement config : editor.getAdapterDefinitions()) {
String uri = config.getAttribute(PageAdaptersExtensionPointConstants.ATTR_NAMESPACE_URI);
if (uri.equals(child.getNamespaceURI())) {
try {
Object obj = config
.createExecutableExtension(PageAdaptersExtensionPointConstants.ATTR_MODEL_FACTORY);
if (obj instanceof IModelFactory) {
IModelFactory factory = (IModelFactory) obj;
factory.getNestedChildrenFromXml(list, childElem, this);
}
}
catch (CoreException e) {
StatusHandler.log(new Status(IStatus.ERROR, ConfigGraphPlugin.PLUGIN_ID,
Messages.AbstractConfigFlowDiagram_ERROR_CREATING_GRAPH, e));
}
}
}
}
if (child.hasChildNodes()) {
list.addAll(getNestedChildrenFromXml(childElem));
}
}
}
return list;
}
private SpringCorePreferences getPreferenceLocation() {
IFile resource = getResourceFile();
SpringCorePreferences prefs = SpringCorePreferences.getProjectPreferences(resource.getProject(),
ConfigGraphPlugin.LEGACY_ID);
return prefs;
}
public Node getReferencedNode(String ref) {
if (ref == null || ref.length() <= 0) {
return null;
}
return refNodeRegistry.get(ref);
}
public IFile getResourceFile() {
return editor.getResourceFile();
}
public StructuredTextEditor getTextEditor() {
return editor.getEditor().getSourcePage();
}
protected void getTransitionsFromXml(Activity source, Activity target, List<Transition> transitions,
List<String> attrs, boolean incoming, boolean primary) {
for (String label : attrs) {
Attr attr = source.getInput().getAttributeNode(label);
if (attr instanceof IDOMAttr) {
Node ref = getReferencedNode(attr.getValue());
if (ref instanceof IDOMElement && target.getInput().equals(ref)) {
Transition trans;
if (incoming) {
trans = new Transition(target, source, (IDOMAttr) attr);
}
else {
trans = new Transition(source, target, (IDOMAttr) attr);
}
if (!primary) {
trans.setLineStyle(Transition.DASHED_CONNECTION);
}
transitions.add(trans);
}
}
}
}
protected SpringConfigContentAssistProcessor getXmlProcessor() {
return editor.getXmlProcessor();
}
@Override
protected void internalSetName() {
setName("diagram"); //$NON-NLS-1$
}
// Move this into ConfigCoreUtils??
public boolean listContainsElement(List<Activity> list, Activity element) {
for (Activity activity : list) {
if (activity.getClass().equals(element.getClass()) && activity.getInput() != null
&& activity.getInput().equals(element.getInput())) {
return true;
}
}
return false;
}
public void refreshModelFromXml() {
modelRegistry.clear();
updateRefNodeRegistry();
updateChildrenFromXml();
updateTransitionsFromXml();
}
@Override
public void setInput(IDOMElement input) {
// no-op
}
private void updateRefNodeRegistry() {
refNodeRegistry = BeansEditorUtils.getReferenceableNodes(getDomDocument(), getResourceFile());
}
}