Bug 577378: [Ltk-Model] Add extensible element content and label
provider

Change-Id: I0ec465227ff7e8b44f082093f48934f4bf84ce8d
diff --git a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/ElementSet.java b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/ElementSet.java
index dfbbd49..ea6eb3f 100644
--- a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/ElementSet.java
+++ b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/ElementSet.java
@@ -153,7 +153,7 @@
 	}
 	
 	public @Nullable IResource getOwningResource(final LtkModelElement<?> element) {
-		if ((element.getElementType() & LtkModelElement.MASK_C2) < LtkModelElement.C2_SOURCE_CHUNK) {
+		if ((element.getElementType() & LtkModelElement.MASK_C12) < LtkModelElement.C12_SOURCE_CHUNK) {
 			IResource resource;
 			resource= element.getAdapter(IResource.class);
 			return resource;
diff --git a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/element/LtkModelElement.java b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/element/LtkModelElement.java
index e0ebcb0..1e32d7c 100644
--- a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/element/LtkModelElement.java
+++ b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/element/LtkModelElement.java
@@ -31,21 +31,22 @@
 public interface LtkModelElement<TModelChild extends LtkModelElement<?>> extends IAdaptable {
 	
 	
-	static final int MASK_C1=            0xf00;
-	static final int SHIFT_C1=           8;
-	static final int MASK_C2=            0xff0;
-	static final int MASK_C3=            0xfff;
+	static final int SHIFT_C1=          8;
+	static final int SHIFT_C2=          4;
+	static final int SHIFT_C3=          0;
+	static final int MASK_C1=           0xF << SHIFT_C1;
+	static final int MASK_C12=          MASK_C1 | 0xF << SHIFT_C2;
+	static final int MASK_C123=         MASK_C12 | 0xF << SHIFT_C3;
 	
-	static final int C1_BUNDLE=          0x100;
-	static final int C1_SOURCE=          0x200;
-	static final int C1_IMPORT=          0x300;
-	static final int C1_CLASS=           0x400;
-	static final int C1_METHOD=          0x500;
-	static final int C1_VARIABLE=        0x600;
-	static final int C1_EMBEDDED=        0x800;
-	
-	static final int C2_SOURCE_FILE=     C1_SOURCE | 0x10;
-	static final int C2_SOURCE_CHUNK=    C1_SOURCE | 0x80;
+	static final int C1_BUNDLE=         0x1 << SHIFT_C1;
+	static final int C1_SOURCE=         0x2 << SHIFT_C1;
+	static final int C12_SOURCE_FILE=       C1_SOURCE | 0x1 << SHIFT_C2;
+	static final int C12_SOURCE_CHUNK=      C1_SOURCE | 0x8 << SHIFT_C2;
+	static final int C1_IMPORT=         0x3 << SHIFT_C1;
+	static final int C1_CLASS=          0x4 << SHIFT_C1;
+	static final int C1_METHOD=         0x5 << SHIFT_C1;
+	static final int C1_VARIABLE=       0x6 << SHIFT_C1;
+	static final int C1_EMBEDDED=       0x8 << SHIFT_C1;
 	
 	
 	String getModelTypeId();
diff --git a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericFragmentSourceUnit.java b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericFragmentSourceUnit.java
index 5281445..b9340cc 100644
--- a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericFragmentSourceUnit.java
+++ b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericFragmentSourceUnit.java
@@ -97,11 +97,11 @@
 	 * {@inheritDoc}
 	 * 
 	 * A source unit of this type is usually of the type
-	 * {@link LtkModelElement#C2_SOURCE_CHUNK C2_SOURCE_CHUNK}.
+	 * {@link LtkModelElement#C12_SOURCE_CHUNK C2_SOURCE_CHUNK}.
 	 */
 	@Override
 	public int getElementType() {
-		return LtkModelElement.C2_SOURCE_CHUNK;
+		return LtkModelElement.C12_SOURCE_CHUNK;
 	}
 	
 	@Override
diff --git a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericResourceSourceUnit.java b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericResourceSourceUnit.java
index 15e5894..945d921 100644
--- a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericResourceSourceUnit.java
+++ b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericResourceSourceUnit.java
@@ -129,11 +129,11 @@
 	 * {@inheritDoc}
 	 * 
 	 * A source unit of this type is usually of the type
-	 * {@link LtkModelElement#C2_SOURCE_FILE C2_SOURCE_FILE}.
+	 * {@link LtkModelElement#C12_SOURCE_FILE C2_SOURCE_FILE}.
 	 */
 	@Override
 	public int getElementType() {
-		return C2_SOURCE_FILE;
+		return C12_SOURCE_FILE;
 	}
 	
 	@Override
diff --git a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericUriSourceUnit.java b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericUriSourceUnit.java
index 9fb093f..bf31dcc 100644
--- a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericUriSourceUnit.java
+++ b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/model/core/impl/GenericUriSourceUnit.java
@@ -95,11 +95,11 @@
 	 * {@inheritDoc}
 	 * 
 	 * A source unit of this type is usually of the type
-	 * {@link LtkModelElement#C2_SOURCE_FILE C2_SOURCE_FILE}.
+	 * {@link LtkModelElement#C12_SOURCE_FILE C2_SOURCE_FILE}.
 	 */
 	@Override
 	public int getElementType() {
-		return LtkModelElement.C2_SOURCE_FILE;
+		return LtkModelElement.C12_SOURCE_FILE;
 	}
 	
 	@Override
diff --git a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/refactoring/core/RefactoringAdapter.java b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/refactoring/core/RefactoringAdapter.java
index 8e837e9..42e1b61 100644
--- a/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/refactoring/core/RefactoringAdapter.java
+++ b/ltk/org.eclipse.statet.ltk.core/src/org/eclipse/statet/ltk/refactoring/core/RefactoringAdapter.java
@@ -539,7 +539,7 @@
 	}
 	
 	public void checkFinalToDelete(final RefactoringStatus result, final LtkModelElement element) throws CoreException {
-		if ((element.getElementType() & LtkModelElement.MASK_C2) == LtkModelElement.C2_SOURCE_FILE) {
+		if ((element.getElementType() & LtkModelElement.MASK_C12) == LtkModelElement.C12_SOURCE_FILE) {
 			if (element instanceof WorkspaceSourceUnit) {
 				checkFinalToDelete(result, ((WorkspaceSourceUnit)element).getResource());
 			}
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/internal/ltk/ui/refactoring/ModelElementTester.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/internal/ltk/ui/refactoring/ModelElementTester.java
index 3482fc9..60624a3 100644
--- a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/internal/ltk/ui/refactoring/ModelElementTester.java
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/internal/ltk/ui/refactoring/ModelElementTester.java
@@ -47,7 +47,7 @@
 			mask= LtkModelElement.MASK_C1;
 		}
 		else if (IS_ELEMENT_C2_TYPE_SELECTION.equals(property)) {
-			mask= LtkModelElement.MASK_C2;
+			mask= LtkModelElement.MASK_C12;
 		}
 		int numSu= 1;
 		String modelType= (String) expectedValue;
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/ElementContentProvider.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/ElementContentProvider.java
new file mode 100644
index 0000000..149f9a1
--- /dev/null
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/ElementContentProvider.java
@@ -0,0 +1,48 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ltk.ui;
+
+import java.util.List;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ltk.model.core.element.LtkModelElement;
+import org.eclipse.statet.ltk.model.core.element.LtkModelElementFilter;
+import org.eclipse.statet.ltk.model.core.element.SourceStructElement;
+
+
+@NonNullByDefault
+public interface ElementContentProvider {
+	
+	
+	default @Nullable SourceStructElement<?, ?> getSourceParent(
+			final SourceStructElement<?, ?> element) {
+		return element.getSourceParent();
+	}
+	
+	default boolean hasSourceChildren(
+			final SourceStructElement<?, ?> element,
+			final @Nullable LtkModelElementFilter<? super SourceStructElement<?, ?>> filter) {
+		return element.hasSourceChildren(filter);
+	}
+	
+	default List<? extends LtkModelElement<?>> getSourceChildren(
+			final SourceStructElement<?, ?> element,
+			final @Nullable LtkModelElementFilter<? super SourceStructElement<?, ?>> filter) {
+		return element.getSourceChildren(filter);
+	}
+	
+}
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/ElementLabelProvider.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/ElementLabelProvider.java
index ccc4c84..8931317 100644
--- a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/ElementLabelProvider.java
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/ElementLabelProvider.java
@@ -36,24 +36,29 @@
 	static final StyledString.Styler TITLE_STYLER= new StyledString.Styler() {
 		@Override
 		public void applyStyles(final TextStyle style) {
-			((StyleRange) style).fontStyle= SWT.BOLD;
+			((StyleRange)style).fontStyle= SWT.BOLD;
 		};
 	};
 	
 	
-	String getText(final LtkModelElement element);
+	default @Nullable Image getImage(final LtkModelElement<?> element) {
+		return null;
+	}
 	
-//	void decorateText(final StringBuilder text, final LtkModelElement element);
+	default @Nullable String getText(final LtkModelElement<?> element) {
+		return null;
+	}
 	
-	StyledString getStyledText(final LtkModelElement element);
+	default @Nullable StyledString getStyledText(final LtkModelElement<?> element) {
+		final var text= getText(element);
+		return (text != null) ? new StyledString(text) : null;
+	}
 	
-	default int @Nullable [] getStyledTextRegions(final LtkModelElement element,
+	default int @Nullable [] getStyledTextRegions(final LtkModelElement<?> element,
 			final int flags, final int[] regions) {
 		return null;
 	}
 	
 //	void decorateStyledText(final StyledString text, final LtkModelElement element);
 	
-	Image getImage(final LtkModelElement element);
-	
 }
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/OutlineContentProvider.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/OutlineContentProvider.java
deleted file mode 100644
index 4a6c490..0000000
--- a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/OutlineContentProvider.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2014, 2021 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.ltk.ui.sourceediting;
-
-import java.util.List;
-
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.Viewer;
-
-import org.eclipse.statet.jcommons.lang.NonNull;
-import org.eclipse.statet.jcommons.lang.NonNullByDefault;
-import org.eclipse.statet.jcommons.lang.Nullable;
-
-import org.eclipse.statet.ltk.core.source.SourceModelStamp;
-import org.eclipse.statet.ltk.model.core.element.LtkModelElementFilter;
-import org.eclipse.statet.ltk.model.core.element.SourceStructElement;
-import org.eclipse.statet.ltk.model.core.element.SourceUnitModelInfo;
-
-
-@NonNullByDefault
-public class OutlineContentProvider implements ITreeContentProvider {
-	
-	
-	private static final @NonNull SourceStructElement<?, ?>[] NO_CHILDREN= new @NonNull SourceStructElement[0];
-	
-	
-	public interface OutlineContent {
-		
-		@Nullable SourceUnitModelInfo getModelInfo(Object inputElement);
-		
-		@Nullable LtkModelElementFilter<? super SourceStructElement<?, ?>> getContentFilter();
-		
-	}
-	
-	
-	private final OutlineContent content;
-	
-	
-	public OutlineContentProvider(final OutlineContent content) {
-		this.content= content;
-	}
-	
-	
-	protected final OutlineContent getContent() {
-		return this.content;
-	}
-	
-	public @Nullable SourceModelStamp getStamp(final Object inputElement) {
-		final SourceUnitModelInfo modelInfo= getContent().getModelInfo(inputElement);
-		return (modelInfo != null) ? modelInfo.getStamp() : null;
-	}
-	
-	@Override
-	public void inputChanged(final Viewer viewer, final @Nullable Object oldInput, final @Nullable Object newInput) {
-	}
-	
-	@Override
-	public void dispose() {
-	}
-	
-	
-	protected SourceStructElement[] getElements(final @Nullable SourceUnitModelInfo modelInfo) {
-		if (modelInfo != null) {
-			final List<? extends SourceStructElement<?, ?>> children= modelInfo.getSourceElement()
-					.getSourceChildren(getContent().getContentFilter());
-			return children.toArray(new @NonNull SourceStructElement[children.size()]);
-		}
-		return NO_CHILDREN;
-	}
-	
-	protected @Nullable SourceStructElement getParent(final SourceStructElement<?, ?> element) {
-		return element.getSourceParent();
-	}
-	
-	protected boolean hasChildren(final @Nullable SourceStructElement<?, ?> element) {
-		return (element != null
-				&& element.hasSourceChildren(getContent().getContentFilter()) );
-	}
-	
-	protected SourceStructElement[] getChildren(final @Nullable SourceStructElement<?, ?> element) {
-		if (element != null) {
-			final List<? extends SourceStructElement> children= element
-					.getSourceChildren(getContent().getContentFilter());
-			return children.toArray(new @NonNull SourceStructElement[children.size()]);
-		}
-		return NO_CHILDREN;
-	}
-	
-	
-	@Override
-	public @NonNull Object[] getElements(final Object inputElement) {
-		return getElements(getContent().getModelInfo(inputElement));
-	}
-	
-	@Override
-	public @Nullable Object getParent(final Object element) {
-		return getParent((SourceStructElement<?, ?>)element);
-	}
-	
-	@Override
-	public boolean hasChildren(final Object element) {
-		return hasChildren((SourceStructElement<?, ?>)element);
-	}
-	
-	@Override
-	public @NonNull Object[] getChildren(final Object element) {
-		return getChildren((SourceStructElement<?, ?>)element);
-	}
-	
-}
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/QuickOutlineInformationControl.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/QuickOutlineInformationControl.java
index b08a880..a5904e1 100644
--- a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/QuickOutlineInformationControl.java
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/QuickOutlineInformationControl.java
@@ -14,9 +14,13 @@
 
 package org.eclipse.statet.ltk.ui.sourceediting;
 
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
+
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Shell;
@@ -36,6 +40,8 @@
 import org.eclipse.statet.ltk.model.core.element.SourceUnitModelInfo;
 import org.eclipse.statet.ltk.ui.LtkActions;
 import org.eclipse.statet.ltk.ui.sourceediting.actions.OpenDeclaration;
+import org.eclipse.statet.ltk.ui.util.ExtModelContentProvider;
+import org.eclipse.statet.ltk.ui.util.ExtModelLabelProvider;
 
 
 /**
@@ -48,7 +54,7 @@
 	protected static final String INHERITED_COLOR_NAME= "org.eclipse.jdt.ui.ColoredLabels.inherited"; //$NON-NLS-1$
 	
 	
-	protected class QuickOutlineContent implements OutlineContentProvider.OutlineContent {
+	protected class QuickOutlineContent implements ExtModelContentProvider.ModelContent {
 		
 		
 		public QuickOutlineContent() {
@@ -69,7 +75,7 @@
 	
 	private final OpenDeclaration opener;
 	
-	private OutlineContentProvider contentProvider;
+	private ITreeContentProvider contentProvider= nonNullLateInit();
 	
 	private boolean requireFullName;
 	
@@ -90,7 +96,7 @@
 	}
 	
 	
-	public abstract String getModelTypeId();
+	public abstract String getModelTypeId(int page);
 	
 	@Override
 	protected IDialogSettings getDialogSettings() {
@@ -128,15 +134,21 @@
 	protected void configureViewer(final TreeViewer viewer) {
 		this.contentProvider= createContentProvider();
 		viewer.setContentProvider(this.contentProvider);
+		viewer.setLabelProvider(createLabelProvider());
 	}
 	
-	protected OutlineContentProvider createContentProvider() {
-		return new OutlineContentProvider(new QuickOutlineContent());
+	protected ITreeContentProvider createContentProvider() {
+		return new ExtModelContentProvider(new QuickOutlineContent());
+	}
+	
+	protected ILabelProvider createLabelProvider() {
+		return new ExtModelLabelProvider(getModelTypeId(0));
 	}
 	
 	protected @Nullable SourceUnitModelInfo getModelInfo(final Object input) {
 		if (input instanceof SourceUnit) {
-			return ((SourceUnit) input).getModelInfo(getModelTypeId(), 0, null);
+			final String modelTypeId= getModelTypeId(getIterationPosition());
+			return ((SourceUnit)input).getModelInfo(modelTypeId, 0, null);
 		}
 		return null;
 	}
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/SourceEditor1.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/SourceEditor1.java
index fce88bc..a777de7 100644
--- a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/SourceEditor1.java
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/SourceEditor1.java
@@ -678,12 +678,12 @@
 			case LtkModelElement.C1_METHOD:
 				return expand(element.getSourceRange(), element.getDocumentationRange());
 			case LtkModelElement.C1_SOURCE:
-				if ((element.getElementType() & LtkModelElement.MASK_C2) == LtkModelElement.C2_SOURCE_CHUNK) {
+				if ((element.getElementType() & LtkModelElement.MASK_C12) == LtkModelElement.C12_SOURCE_CHUNK) {
 					return expand(element.getSourceRange(), element.getDocumentationRange());
 				}
 				return null;
 			case LtkModelElement.C1_VARIABLE:
-				if ((element.getSourceParent().getElementType() & LtkModelElement.MASK_C2) == LtkModelElement.C2_SOURCE_FILE) {
+				if ((element.getSourceParent().getElementType() & LtkModelElement.MASK_C12) == LtkModelElement.C12_SOURCE_FILE) {
 					return expand(element.getSourceRange(), element.getDocumentationRange());
 				}
 				//$FALL-THROUGH$
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/SourceEditor1OutlinePage.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/SourceEditor1OutlinePage.java
index 39aeed4..1fd4883 100644
--- a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/SourceEditor1OutlinePage.java
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/sourceediting/SourceEditor1OutlinePage.java
@@ -68,6 +68,7 @@
 import org.eclipse.statet.ltk.ui.LTKInputData;
 import org.eclipse.statet.ltk.ui.ModelElementInputListener;
 import org.eclipse.statet.ltk.ui.SelectionWithElementInfoListener;
+import org.eclipse.statet.ltk.ui.util.ExtModelContentProvider;
 
 
 /**
@@ -80,7 +81,7 @@
 				IPostSelectionProvider, ModelElementInputListener<LtkModelElement<?>> {
 	
 	
-	protected class PageOutlineContent implements OutlineContentProvider.OutlineContent {
+	protected class PageOutlineContent implements ExtModelContentProvider.ModelContent {
 		
 		
 		public PageOutlineContent() {
@@ -99,7 +100,7 @@
 		
 	}
 	
-	public class AstContentProvider extends OutlineContentProvider {
+	public class AstContentProvider extends ExtModelContentProvider {
 		
 		
 		public AstContentProvider() {
@@ -229,7 +230,7 @@
 	
 	private final SourceEditor1 editor;
 	private final String mainType;
-	private OutlineContentProvider contentProvider;
+	private ExtModelContentProvider contentProvider;
 	
 	private @Nullable SourceModelStamp currentModelStamp;
 	
@@ -279,8 +280,8 @@
 		getViewer().setInput(this.inputUnit);
 	}
 	
-	protected OutlineContentProvider createContentProvider() {
-		return new OutlineContentProvider(new PageOutlineContent());
+	protected ExtModelContentProvider createContentProvider() {
+		return new ExtModelContentProvider(new PageOutlineContent());
 	}
 	
 	@Override
@@ -402,7 +403,7 @@
 					selectedElement= currentSelection.getFirstElement();
 				}
 				while (element != null 
-						&& (element.getElementType() & LtkModelElement.MASK_C2) != LtkModelElement.C2_SOURCE_FILE) {
+						&& (element.getElementType() & LtkModelElement.MASK_C12) != LtkModelElement.C12_SOURCE_FILE) {
 					if (selectedElement != null && element.equals(selectedElement)) {
 						return;
 					}
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelContentProvider.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelContentProvider.java
new file mode 100644
index 0000000..4bf2f08
--- /dev/null
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelContentProvider.java
@@ -0,0 +1,193 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ltk.ui.util;
+
+import java.util.IdentityHashMap;
+import java.util.List;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.statet.jcommons.lang.Disposable;
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ltk.core.source.SourceModelStamp;
+import org.eclipse.statet.ltk.model.core.LtkModels;
+import org.eclipse.statet.ltk.model.core.element.EmbeddingForeignElement;
+import org.eclipse.statet.ltk.model.core.element.LtkModelElement;
+import org.eclipse.statet.ltk.model.core.element.LtkModelElementFilter;
+import org.eclipse.statet.ltk.model.core.element.SourceStructElement;
+import org.eclipse.statet.ltk.model.core.element.SourceUnitModelInfo;
+import org.eclipse.statet.ltk.ui.ElementContentProvider;
+
+
+@NonNullByDefault
+public class ExtModelContentProvider implements ITreeContentProvider {
+	
+	
+	private static final @NonNull SourceStructElement<?, ?>[] NO_CHILDREN= new SourceStructElement[0];
+	
+	protected static final ElementContentProvider NO_PROVIDER= new ElementContentProvider() {
+	};
+	
+	
+	private final IdentityHashMap<String, ElementContentProvider> providers= new IdentityHashMap<>(4);
+	
+	
+	public interface ModelContent {
+		
+		@Nullable SourceUnitModelInfo getModelInfo(Object inputElement);
+		
+		@Nullable LtkModelElementFilter<? super SourceStructElement<?, ?>> getContentFilter();
+		
+	}
+	
+	
+	private final ModelContent content;
+	
+	
+	public ExtModelContentProvider(final ModelContent content) {
+		this.content= content;
+	}
+	
+	@Override
+	public void dispose() {
+		for (final var provider : this.providers.values()) {
+			if (provider instanceof Disposable) {
+				((Disposable)provider).dispose();
+			}
+		}
+		this.providers.clear();
+	}
+	
+	
+	protected final ModelContent getContent() {
+		return this.content;
+	}
+	
+	public @Nullable SourceModelStamp getStamp(final Object inputElement) {
+		final SourceUnitModelInfo modelInfo= getContent().getModelInfo(inputElement);
+		return (modelInfo != null) ? modelInfo.getStamp() : null;
+	}
+	
+	@Override
+	public void inputChanged(final Viewer viewer, final @Nullable Object oldInput, final @Nullable Object newInput) {
+	}
+	
+	
+	protected @Nullable ElementContentProvider getElementContentProvider(final @Nullable String modelId) {
+		if (modelId == null) {
+			return null;
+		}
+		var provider= this.providers.get(modelId);
+		if (provider == null) {
+			provider= LtkModels.getModelAdapter(modelId, ElementContentProvider.class);
+			if (provider == null) {
+				provider= NO_PROVIDER;
+			}
+			this.providers.put(modelId, provider);
+		}
+		return (provider != NO_PROVIDER) ? provider : null;
+	}
+	
+	protected void addProvider(final String modelId, final ElementContentProvider provider) {
+		this.providers.put(modelId, provider);
+	}
+	
+	
+	private boolean skipToForeignElement(final @Nullable SourceStructElement<?, ?> element) {
+		return (element != null
+				&& (element.getElementType() & LtkModelElement.MASK_C1) == LtkModelElement.C1_EMBEDDED );
+	}
+	
+	protected boolean skipInTree(final SourceStructElement<?, ?> element) {
+		return false;
+	}
+	
+	protected @NonNull SourceStructElement<?, ?>[] getElements(final @Nullable SourceUnitModelInfo modelInfo) {
+		if (modelInfo != null) {
+			final var children= modelInfo.getSourceElement()
+					.getSourceChildren(getContent().getContentFilter());
+			return children.toArray(new @NonNull SourceStructElement[children.size()]);
+		}
+		return NO_CHILDREN;
+	}
+	
+	protected @Nullable SourceStructElement<?, ?> getParent(final SourceStructElement<?, ?> element) {
+		var parent= element.getSourceParent();
+		while (parent != null && (skipToForeignElement(parent) || skipInTree(parent))) {
+			parent= parent.getSourceParent();
+		}
+		return parent;
+	}
+	
+	protected boolean hasChildren(@Nullable SourceStructElement<?, ?> element) {
+		if (skipToForeignElement(element)) {
+			element= ((EmbeddingForeignElement<?, ?>)element).getForeignElement();
+		}
+		ElementContentProvider provider;
+		if (element != null) {
+			if ((provider= getElementContentProvider(element.getModelTypeId())) != null) {
+				return provider.hasSourceChildren(element, getContent().getContentFilter());
+			}
+			else {
+				return element.hasSourceChildren(getContent().getContentFilter());
+			}
+		}
+		return false;
+	}
+	
+	protected @NonNull SourceStructElement<?, ?>[] getChildren(@Nullable SourceStructElement<?, ?> element) {
+		if (skipToForeignElement(element)) {
+			element= ((EmbeddingForeignElement<?, ?>)element).getForeignElement();
+		}
+		ElementContentProvider provider;
+		if (element != null) {
+			List<? extends LtkModelElement<?>> children;
+			if ((provider= getElementContentProvider(element.getModelTypeId())) != null) {
+				children= provider.getSourceChildren(element, getContent().getContentFilter());
+			}
+			else {
+				children= element.getSourceChildren(getContent().getContentFilter());
+			}
+			return children.toArray(new @NonNull SourceStructElement[children.size()]);
+		}
+		return NO_CHILDREN;
+	}
+	
+	
+	@Override
+	public @NonNull Object[] getElements(final Object inputElement) {
+		return getElements(getContent().getModelInfo(inputElement));
+	}
+	
+	@Override
+	public @Nullable Object getParent(final Object element) {
+		return getParent((SourceStructElement<?, ?>)element);
+	}
+	
+	@Override
+	public boolean hasChildren(final Object element) {
+		return hasChildren((SourceStructElement<?, ?>)element);
+	}
+	
+	@Override
+	public @NonNull Object[] getChildren(final Object element) {
+		return getChildren((SourceStructElement<?, ?>)element);
+	}
+
+}
diff --git a/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelLabelProvider.java b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelLabelProvider.java
new file mode 100644
index 0000000..c5d4d2b
--- /dev/null
+++ b/ltk/org.eclipse.statet.ltk.ui/src/org/eclipse/statet/ltk/ui/util/ExtModelLabelProvider.java
@@ -0,0 +1,201 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.ltk.ui.util;
+
+import java.util.IdentityHashMap;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.statet.jcommons.lang.Disposable;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ltk.model.core.LtkModels;
+import org.eclipse.statet.ltk.model.core.element.EmbeddingForeignElement;
+import org.eclipse.statet.ltk.model.core.element.LtkModelElement;
+import org.eclipse.statet.ltk.ui.ElementLabelProvider;
+
+
+@NonNullByDefault
+public class ExtModelLabelProvider extends StyledCellLabelProvider
+		implements ILabelProvider, ElementLabelProvider, Disposable {
+	
+	
+	protected static final ElementLabelProvider NO_PROVIDER= new ElementLabelProvider() {
+	};
+	
+	
+	private final IdentityHashMap<String, ElementLabelProvider> providers= new IdentityHashMap<>(4);
+	
+	private @Nullable String modelTypeId;
+	
+	
+	public ExtModelLabelProvider(final String modelTypeId) {
+		this.modelTypeId= modelTypeId;
+	}
+	
+	public ExtModelLabelProvider() {
+	}
+	
+	
+	@Override
+	public void dispose() {
+		for (final var provider : this.providers.values()) {
+			if (provider instanceof Disposable) {
+				((Disposable)provider).dispose();
+			}
+		}
+		this.providers.clear();
+		
+		super.dispose();
+	}
+	
+	
+	protected @Nullable ElementLabelProvider getElementLabelProvider(final @Nullable String modelId) {
+		if (modelId == null) {
+			return null;
+		}
+		var provider= this.providers.get(modelId);
+		if (provider == null) {
+			provider= LtkModels.getModelAdapter(modelId, ElementLabelProvider.class);
+			if (provider == null) {
+				provider= NO_PROVIDER;
+			}
+			this.providers.put(modelId, provider);
+		}
+		return (provider != NO_PROVIDER) ? provider : null;
+	}
+	
+	protected void addProvider(final String modelId, final ElementLabelProvider provider) {
+		this.providers.put(modelId, provider);
+	}
+	
+	
+	@Override
+	public @Nullable Image getImage(final @Nullable Object element) {
+		if (element instanceof LtkModelElement) {
+			return getImage((LtkModelElement<?>)element);
+		}
+		return null;
+	}
+	
+	@Override
+	public @Nullable String getText(final @Nullable Object element) {
+		if (element instanceof LtkModelElement) {
+			return getText((LtkModelElement<?>)element);
+		}
+		return null;
+	}
+	
+	
+	@Override
+	public @Nullable Image getImage(final LtkModelElement<?> element) {
+		final boolean embedding= ((element.getElementType() & LtkModelElement.MASK_C1) == LtkModelElement.C1_EMBEDDED);
+		Image image= null;
+		ElementLabelProvider provider;
+		if (image == null && embedding && this.modelTypeId != element.getModelTypeId()
+				&& (provider= getElementLabelProvider(this.modelTypeId)) != null) {
+			image= provider.getImage(element);
+		}
+		if (image == null
+				&& (provider= getElementLabelProvider(element.getModelTypeId())) != null) {
+			image= provider.getImage(element);
+		}
+		if (image == null && embedding) {
+			final var embeddedElement= ((EmbeddingForeignElement<?, ?>)element).getForeignElement();
+			if (embeddedElement != null
+					&& (provider= getElementLabelProvider(embeddedElement.getModelTypeId())) != null) {
+				image= provider.getImage(embeddedElement);
+			}
+		}
+		return image;
+	}
+	
+	@Override
+	public String getText(final LtkModelElement<?> element) {
+		final boolean embedding= ((element.getElementType() & LtkModelElement.MASK_C1) == LtkModelElement.C1_EMBEDDED);
+		String text= null;
+		ElementLabelProvider provider;
+		if (text == null && embedding && this.modelTypeId != element.getModelTypeId()
+				&& (provider= getElementLabelProvider(this.modelTypeId)) != null) {
+			text= provider.getText(element);
+		}
+		if (text == null
+				&& (provider= getElementLabelProvider(element.getModelTypeId())) != null) {
+			text= provider.getText(element);
+		}
+		if (text == null && embedding) {
+			final var embeddedElement= ((EmbeddingForeignElement<?, ?>)element).getForeignElement();
+			if (embeddedElement != null
+					&& (provider= getElementLabelProvider(embeddedElement.getModelTypeId())) != null) {
+				text= provider.getText(embeddedElement);
+			}
+		}
+		if (text == null) {
+			text= element.getElementName().getDisplayName();
+		}
+		return text;
+	}
+	
+	@Override
+	public StyledString getStyledText(final LtkModelElement<?> element) {
+		final boolean embedding= ((element.getElementType() & LtkModelElement.MASK_C1) == LtkModelElement.C1_EMBEDDED);
+		StyledString text= null;
+		ElementLabelProvider provider;
+		if (text == null && embedding && this.modelTypeId != element.getModelTypeId()
+				&& (provider= getElementLabelProvider(this.modelTypeId)) != null) {
+			text= provider.getStyledText(element);
+		}
+		if (text == null
+				&& (provider= getElementLabelProvider(element.getModelTypeId())) != null) {
+			text= provider.getStyledText(element);
+		}
+		if (text == null && embedding) {
+			final var embeddedElement= ((EmbeddingForeignElement<?, ?>)element).getForeignElement();
+			if (embeddedElement != null
+					&& (provider= getElementLabelProvider(embeddedElement.getModelTypeId())) != null) {
+				text= provider.getStyledText(embeddedElement);
+			}
+		}
+		if (text == null) {
+			text= new StyledString(element.getElementName().getDisplayName());
+		}
+		return text;
+	}
+	
+	
+	@Override
+	public void update(final ViewerCell cell) {
+		final Object cellElement= cell.getElement();
+		if (cellElement instanceof LtkModelElement) {
+			final var element= (LtkModelElement<?>)cellElement;
+			cell.setImage(getImage(element));
+			final StyledString styledText= getStyledText(element);
+			cell.setText(styledText.getString());
+			cell.setStyleRanges(styledText.getStyleRanges());
+		}
+		else {
+			cell.setImage(null);
+			cell.setText(cellElement.toString());
+			cell.setStyleRanges(null);
+		}
+		super.update(cell);
+	}
+	
+}