Bug 316371 - [ModelTooling] ModelEditor should be multipart editors which allows to edit xml or via UI
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/OSGI-INF/images.properties b/bundles/org.eclipse.e4.tools.emf.ui/OSGI-INF/images.properties
index 769230b..471396a 100644
--- a/bundles/org.eclipse.e4.tools.emf.ui/OSGI-INF/images.properties
+++ b/bundles/org.eclipse.e4.tools.emf.ui/OSGI-INF/images.properties
@@ -83,6 +83,7 @@
 IMG_org.eclipse.e4.tools.emf.ui.obj16.zoom=/icons/full/obj16/zoom.png
 IMG_org.eclipse.e4.tools.emf.ui.obj16.application_form=/icons/full/obj16/application_form.png
 IMG_org.eclipse.e4.tools.emf.ui.obj16.chart_organisation=/icons/full/obj16/chart_organisation.png
+IMG_org.eclipse.e4.tools.emf.ui.obj16.error_obj=/icons/full/obj16/error_obj.gif
 
 IMG_org.eclipse.e4.tools.emf.ui.wizban.fieldrefact_wiz=/icons/full/wizban/fieldrefact_wiz.png
 IMG_org.eclipse.e4.tools.emf.ui.wizban.import_wiz=/icons/full/wizban/import_wiz.png
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/icons/full/obj16/error_obj.gif b/bundles/org.eclipse.e4.tools.emf.ui/icons/full/obj16/error_obj.gif
new file mode 100644
index 0000000..0bc6068
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools.emf.ui/icons/full/obj16/error_obj.gif
Binary files differ
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/common/IModelResource.java b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/common/IModelResource.java
index 4c5d0ac..0aead91 100644
--- a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/common/IModelResource.java
+++ b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/common/IModelResource.java
@@ -12,20 +12,29 @@
 
 import org.eclipse.core.databinding.observable.list.IObservableList;
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.edit.domain.EditingDomain;
 
 public interface IModelResource {
 	public IObservableList getRoot();
+
 	public boolean isSaveable();
+
 	public IStatus save();
+
 	public EditingDomain getEditingDomain();
+
 	public boolean isDirty();
-	
+
+	public void replaceRoot(EObject eobject);
+
 	public void addModelListener(ModelListener listener);
+
 	public void removeModelListener(ModelListener listener);
-	
+
 	public interface ModelListener {
 		public void dirtyChanged();
+
 		public void commandStackChanged();
 	}
 }
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/common/XMIModelResource.java b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/common/XMIModelResource.java
index d6b47cf..c9f53fb 100644
--- a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/common/XMIModelResource.java
+++ b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/common/XMIModelResource.java
@@ -11,21 +11,27 @@
 package org.eclipse.e4.tools.emf.ui.common;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.EventObject;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.eclipse.core.databinding.observable.list.IObservableList;
-import org.eclipse.core.databinding.observable.list.WritableList;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.e4.ui.internal.workbench.E4XMIResourceFactory;
 import org.eclipse.emf.common.command.BasicCommandStack;
+import org.eclipse.emf.common.command.Command;
 import org.eclipse.emf.common.command.CommandStackListener;
+import org.eclipse.emf.common.command.CompoundCommand;
 import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.databinding.edit.EMFEditProperties;
+import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.ecore.resource.ResourceSet;
 import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.edit.command.AddCommand;
+import org.eclipse.emf.edit.command.RemoveCommand;
 import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
 import org.eclipse.emf.edit.domain.EditingDomain;
 import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
@@ -35,6 +41,7 @@
 	private Resource resource;
 	private List<ModelListener> listeners = new ArrayList<IModelResource.ModelListener>();
 	private boolean dirty;
+	private IObservableList list;
 
 	public XMIModelResource(URI uri) {
 		ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
@@ -54,11 +61,22 @@
 	}
 
 	public IObservableList getRoot() {
-		WritableList list = new WritableList();
-		list.add(resource.getContents().get(0));
+		if (list != null) {
+			return list;
+		}
+
+		list = EMFEditProperties.resource(getEditingDomain()).observe(resource);
+
 		return list;
 	}
 
+	public void replaceRoot(EObject eobject) {
+		Command cmdRemove = new RemoveCommand(getEditingDomain(), resource.getContents(), list.get(0));
+		Command cmdAdd = new AddCommand(getEditingDomain(), resource.getContents(), eobject);
+		CompoundCommand cmd = new CompoundCommand(Arrays.asList(cmdRemove, cmdAdd));
+		getEditingDomain().getCommandStack().execute(cmd);
+	}
+
 	public EditingDomain getEditingDomain() {
 		return editingDomain;
 	}
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/ResourceProvider.java b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/ResourceProvider.java
index aec4dbb..41c8eee 100644
--- a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/ResourceProvider.java
+++ b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/ResourceProvider.java
@@ -90,6 +90,7 @@
 	public static final String IMG_Obj16_zoom = "IMG_org.eclipse.e4.tools.emf.ui.obj16.zoom"; //$NON-NLS-1$
 	public static final String IMG_Obj16_application_form = "IMG_org.eclipse.e4.tools.emf.ui.obj16.application_form"; //$NON-NLS-1$
 	public static final String IMG_Obj16_chart_organisation = "IMG_org.eclipse.e4.tools.emf.ui.obj16.chart_organisation"; //$NON-NLS-1$
+	public static final String IMG_Obj16_error_obj = "IMG_org.eclipse.e4.tools.emf.ui.obj16.error_obj"; //$NON-NLS-1$
 
 	public static final String IMG_Wizban16_fieldrefact_wiz = "IMG_org.eclipse.e4.tools.emf.ui.wizban.fieldrefact_wiz"; //$NON-NLS-1$
 	public static final String IMG_Wizban16_import_wiz = "IMG_org.eclipse.e4.tools.emf.ui.wizban.import_wiz"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/ModelEditor.java b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/ModelEditor.java
index c11030d..78a07d7 100644
--- a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/ModelEditor.java
+++ b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/ModelEditor.java
@@ -114,8 +114,9 @@
 import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VWindowEditor;
 import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VWindowSharedElementsEditor;
 import org.eclipse.e4.tools.emf.ui.internal.common.component.virtual.VWindowTrimEditor;
+import org.eclipse.e4.tools.emf.ui.internal.common.xml.AnnotationAccess;
 import org.eclipse.e4.tools.emf.ui.internal.common.xml.ColorManager;
-import org.eclipse.e4.tools.emf.ui.internal.common.xml.EMFDocument;
+import org.eclipse.e4.tools.emf.ui.internal.common.xml.EMFDocumentResourceMediator;
 import org.eclipse.e4.tools.emf.ui.internal.common.xml.XMLConfiguration;
 import org.eclipse.e4.tools.emf.ui.internal.common.xml.XMLPartitionScanner;
 import org.eclipse.e4.tools.services.IClipboardService;
@@ -144,6 +145,7 @@
 import org.eclipse.emf.ecore.EClassifier;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
 import org.eclipse.emf.ecore.util.EcoreUtil;
 import org.eclipse.emf.edit.command.AddCommand;
 import org.eclipse.emf.edit.command.CommandParameter;
@@ -158,9 +160,13 @@
 import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
 import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor;
 import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.Position;
 import org.eclipse.jface.text.rules.FastPartitioner;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.AnnotationModel;
 import org.eclipse.jface.text.source.SourceViewer;
 import org.eclipse.jface.text.source.VerticalRuler;
 import org.eclipse.jface.viewers.ISelectionChangedListener;
@@ -218,7 +224,7 @@
 	public static final String VIRTUAL_MENUELEMENTS = ModelEditor.class.getName() + ".VIRTUAL_MENUELEMENTS"; //$NON-NLS-1$
 	public static final String VIRTUAL_ROOT_CONTEXTS = ModelEditor.class.getName() + ".VIRTUAL_ROOT_CONTEXTS"; //$NON-NLS-1$
 
-	private static final int VERTICAL_RULER_WIDTH = 12;
+	private static final int VERTICAL_RULER_WIDTH = 20;
 
 	private Map<EClass, AbstractComponentEditor> editorMap = new HashMap<EClass, AbstractComponentEditor>();
 	private Map<String, AbstractComponentEditor> virtualEditors = new HashMap<String, AbstractComponentEditor>();
@@ -258,7 +264,7 @@
 
 	private final IResourcePool resourcePool;
 
-	private EMFDocument emfDocumentProvider;
+	private EMFDocumentResourceMediator emfDocumentProvider;
 
 	public ModelEditor(Composite composite, IEclipseContext context, IModelResource modelProvider, IProject project, final IResourcePool resourcePool) {
 		this.resourcePool = resourcePool;
@@ -296,7 +302,7 @@
 		item.setControl(createFormTab(folder));
 		item.setImage(resourcePool.getImageUnchecked(ResourceProvider.IMG_Obj16_application_form));
 
-		emfDocumentProvider = new EMFDocument(modelProvider);
+		emfDocumentProvider = new EMFDocumentResourceMediator(modelProvider);
 
 		item = new CTabItem(folder, SWT.NONE);
 		item.setText(messages.ModelEditor_XMI);
@@ -315,17 +321,41 @@
 	}
 
 	private Control createXMITab(Composite composite) {
-		VerticalRuler verticalRuler = new VerticalRuler(VERTICAL_RULER_WIDTH);
+
+		final AnnotationModel model = new AnnotationModel();
+		VerticalRuler verticalRuler = new VerticalRuler(VERTICAL_RULER_WIDTH, new AnnotationAccess(resourcePool));
 		ColorManager colorManager = new ColorManager();
 		int styles = SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION;
 		SourceViewer viewer = new SourceViewer(composite, verticalRuler, styles);
 		viewer.configure(new XMLConfiguration(colorManager));
-		viewer.setEditable(false);
-		IDocument document = emfDocumentProvider.getDocument();
+		viewer.setEditable(project != null);
+
+		final IDocument document = emfDocumentProvider.getDocument();
 		IDocumentPartitioner partitioner = new FastPartitioner(new XMLPartitionScanner(), new String[] { XMLPartitionScanner.XML_TAG, XMLPartitionScanner.XML_COMMENT });
 		partitioner.connect(document);
 		document.setDocumentPartitioner(partitioner);
 		viewer.setDocument(document);
+		verticalRuler.setModel(model);
+
+		emfDocumentProvider.setValidationChangedCallback(new Runnable() {
+
+			public void run() {
+				model.removeAllAnnotations();
+
+				for (Diagnostic d : emfDocumentProvider.getErrorList()) {
+					Annotation a = new Annotation("e4xmi.error", false, d.getMessage()); //$NON-NLS-1$
+					int l;
+					try {
+						l = document.getLineOffset(d.getLine() - 1);
+						model.addAnnotation(a, new Position(l));
+					} catch (BadLocationException e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					}
+				}
+
+			}
+		});
 
 		return viewer.getControl();
 	}
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/AnnotationAccess.java b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/AnnotationAccess.java
new file mode 100644
index 0000000..0f4ccbd
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/AnnotationAccess.java
@@ -0,0 +1,61 @@
+package org.eclipse.e4.tools.emf.ui.internal.common.xml;
+
+import org.eclipse.e4.tools.emf.ui.internal.ResourceProvider;
+import org.eclipse.e4.tools.services.IResourcePool;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationAccess;
+import org.eclipse.jface.text.source.IAnnotationAccessExtension;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+
+public class AnnotationAccess implements IAnnotationAccess, IAnnotationAccessExtension {
+	private IResourcePool pool;
+
+	public AnnotationAccess(IResourcePool pool) {
+		this.pool = pool;
+	}
+
+	public String getTypeLabel(Annotation annotation) {
+		return annotation.getText();
+	}
+
+	public int getLayer(Annotation annotation) {
+		return IAnnotationAccessExtension.DEFAULT_LAYER;
+	}
+
+	public void paint(Annotation annotation, GC gc, Canvas canvas, Rectangle bounds) {
+		gc.drawImage(pool.getImageUnchecked(ResourceProvider.IMG_Obj16_error_obj), bounds.x, bounds.y);
+	}
+
+	public boolean isPaintable(Annotation annotation) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isSubtype(Object annotationType, Object potentialSupertype) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public Object[] getSupertypes(Object annotationType) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public Object getType(Annotation annotation) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public boolean isMultiLine(Annotation annotation) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isTemporary(Annotation annotation) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+}
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/EMFDocument.java b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/EMFDocument.java
deleted file mode 100644
index 4732550..0000000
--- a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/EMFDocument.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.eclipse.e4.tools.emf.ui.internal.common.xml;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import org.eclipse.e4.tools.emf.ui.common.IModelResource;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
-import org.eclipse.jface.text.Document;
-
-public class EMFDocument {
-	private IModelResource modelResource;
-	private Document document;
-
-	public EMFDocument(IModelResource modelResource) {
-		this.modelResource = modelResource;
-		this.document = new Document();
-		updateFromEMF();
-	}
-
-	public void updateFromEMF() {
-		this.document.set(toXMI((EObject) modelResource.getRoot().get(0)));
-	}
-
-	public Document getDocument() {
-		return document;
-	}
-
-	private String toXMI(EObject root) {
-		XMIResourceImpl resource = new XMIResourceImpl();
-		resource.getContents().add(EcoreUtil.copy(root));
-		StringWriter writer = new StringWriter();
-		try {
-			resource.save(writer, null);
-		} catch (IOException e) {
-			// TODO Auto-generated catch block
-			e.printStackTrace();
-		}
-		return writer.toString();
-	}
-}
diff --git a/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/EMFDocumentResourceMediator.java b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/EMFDocumentResourceMediator.java
new file mode 100644
index 0000000..1907ecf
--- /dev/null
+++ b/bundles/org.eclipse.e4.tools.emf.ui/src/org/eclipse/e4/tools/emf/ui/internal/common/xml/EMFDocumentResourceMediator.java
@@ -0,0 +1,94 @@
+package org.eclipse.e4.tools.emf.ui.internal.common.xml;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.eclipse.e4.tools.emf.ui.common.IModelResource;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocumentListener;
+import org.xml.sax.InputSource;
+
+public class EMFDocumentResourceMediator {
+	private IModelResource modelResource;
+	private Document document;
+	private boolean updateFromEMF;
+	private List<Diagnostic> errorList = new ArrayList<Diagnostic>();
+	private Runnable documentValidationChanged;
+
+	public EMFDocumentResourceMediator(final IModelResource modelResource) {
+		this.modelResource = modelResource;
+		this.document = new Document();
+		this.document.addDocumentListener(new IDocumentListener() {
+
+			public void documentChanged(DocumentEvent event) {
+				if (updateFromEMF) {
+					return;
+				}
+
+				String doc = document.get();
+				XMIResourceImpl res = new XMIResourceImpl();
+				try {
+					res.load(new InputSource(new StringReader(doc)), null);
+					modelResource.replaceRoot(res.getContents().get(0));
+					errorList.clear();
+					if (documentValidationChanged != null) {
+						documentValidationChanged.run();
+					}
+				} catch (IOException e) {
+					errorList = res.getErrors();
+					if (documentValidationChanged != null) {
+						documentValidationChanged.run();
+					}
+
+				}
+			}
+
+			public void documentAboutToBeChanged(DocumentEvent event) {
+
+			}
+		});
+		updateFromEMF();
+	}
+
+	public void setValidationChangedCallback(Runnable runnable) {
+		documentValidationChanged = runnable;
+	}
+
+	public List<Diagnostic> getErrorList() {
+		return Collections.unmodifiableList(errorList);
+	}
+
+	public void updateFromEMF() {
+		try {
+			updateFromEMF = true;
+			this.document.set(toXMI((EObject) modelResource.getRoot().get(0)));
+		} finally {
+			updateFromEMF = false;
+		}
+	}
+
+	public Document getDocument() {
+		return document;
+	}
+
+	private String toXMI(EObject root) {
+		XMIResourceImpl resource = new XMIResourceImpl();
+		resource.getContents().add(EcoreUtil.copy(root));
+		StringWriter writer = new StringWriter();
+		try {
+			resource.save(writer, null);
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return writer.toString();
+	}
+}