You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2017/10/27 13:35:13 UTC

[isis] branch dev/2.0.0/ISIS-1740-where-am-i created (now 38a81da)

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a change to branch dev/2.0.0/ISIS-1740-where-am-i
in repository https://gitbox.apache.org/repos/asf/isis.git.


      at 38a81da  ISIS-1740 initial commit of prototype

This branch includes the following new commits:

     new 38a81da  ISIS-1740 initial commit of prototype

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


-- 
To stop receiving notification emails like this one, please contact
['"commits@isis.apache.org" <co...@isis.apache.org>'].

[isis] 01/01: ISIS-1740 initial commit of prototype

Posted by ah...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch dev/2.0.0/ISIS-1740-where-am-i
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 38a81da3c63096c523d0c59c276a4efad1d64ba0
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Oct 27 15:34:55 2017 +0200

    ISIS-1740 initial commit of prototype
---
 .../org/apache/isis/applib/annotation/Parent.java  |  34 ++++++
 .../metamodel/util/pchain/CachingParentChain.java  |  66 +++++++++++
 .../core/metamodel/util/pchain/ParentChain.java    |  99 ++++++++++++++++
 .../metamodel/util/pchain/SimpleParentChain.java   |  75 +++++++++++++
 .../isis/core/metamodel/util/pchain/SoftCache.java | 124 +++++++++++++++++++++
 .../model/models/whereami/WhereAmIModel.java       |  38 +++++++
 .../models/whereami/WhereAmIModelDefault.java      |  68 +++++++++++
 .../viewer/wicket/ui/pages/entity/EntityPage.css   |  29 +++++
 .../viewer/wicket/ui/pages/entity/EntityPage.html  |   7 +-
 .../viewer/wicket/ui/pages/entity/EntityPage.java  |  68 ++++++++---
 10 files changed, 594 insertions(+), 14 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Parent.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Parent.java
new file mode 100644
index 0000000..6e4a7d8
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Parent.java
@@ -0,0 +1,34 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.applib.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Tells the framework which method or field to use in order to construct a navigable chain of
+ * parent domain object instances. This annotation can only be used once per class declaration. 
+ * 
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface Parent {
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/CachingParentChain.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/CachingParentChain.java
new file mode 100644
index 0000000..630e928
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/CachingParentChain.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.core.metamodel.util.pchain;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+class CachingParentChain extends SimpleParentChain {
+
+	private final SoftCache<Class<?>, MethodHandle> cache = new SoftCache<>();
+
+	@Override
+	public Object parentOf(Object node) {
+		if(node==null)
+			return null;
+		
+		final MethodHandle mh = cache.computeIfAbsent(node.getClass(), 
+				key->{
+					try {
+						return methodHandleOf(node);
+					} catch (IllegalAccessException e) {
+						e.printStackTrace();
+						return null;
+					}
+				});
+		
+		if(mh==null)
+			return null;
+		
+		try {
+			return mh.invoke(node);
+		} catch (Throwable e) {
+			e.printStackTrace();
+			return null;
+		}
+		
+	}
+	
+	protected static MethodHandle methodHandleOf(Object node) throws IllegalAccessException{
+		final Method getter = parentGetterOf(node);
+		return getter!=null ? handleOf(getter) : null;
+	}
+
+	public static MethodHandle handleOf(Method m) throws IllegalAccessException {
+		return MethodHandles.publicLookup().unreflect(m);
+	}
+	
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/ParentChain.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/ParentChain.java
new file mode 100644
index 0000000..31ae507
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/ParentChain.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ 
+package org.apache.isis.core.metamodel.util.pchain;
+
+import java.lang.reflect.Method;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.isis.applib.annotation.Parent;
+import org.apache.isis.core.commons.reflection.Reflect;
+
+/**
+ * Represents a unidirectional linked ordered set of Pojos (chain), where the chain 
+ * starts at startNode. Each subsequent node is linked via de-referencing a 
+ * singular field (or no-arg method) that is annotated with {@code @Parent}.
+ * <br/>
+ * 
+ * startNode --@Parent--&gt; node2 --@Parent--&gt; node3 ...
+ * 
+ * @author ahuber@apache.org
+ *
+ */
+public interface ParentChain {
+	
+	static ParentChain simple() {
+		return new SimpleParentChain();
+	}
+	
+	static ParentChain caching() {
+		return new CachingParentChain();
+	}
+	
+	public Object parentOf(Object node);
+	
+	static boolean providesParent(Method m) {
+		if(!Reflect.isNoArg(m))
+			return false;
+		if(!Reflect.isPublic(m))
+			return false;
+		if(Reflect.isVoid(m)) 
+			return false;
+		if(Reflect.isPrimitive(m.getReturnType())) 
+			return false;
+		
+		if(m.getName().equals("parent"))
+			return true;
+		
+		if(m.isAnnotationPresent(Parent.class))
+			return true;
+		
+		return false;
+	}
+
+	default Stream<Object> streamParentChainOf(Object startNode){
+		final Set<Object> chain = new LinkedHashSet<>();
+		
+		chain.add(startNode);
+		
+		Object next = startNode;
+		
+		while((next = parentOf(next))!=null) {
+			final boolean doContinue = chain.add(next);
+			if(!doContinue)
+				break;
+		}
+		
+		return chain.stream().skip(1);
+	}
+	
+	default Stream<Object> streamReversedParentChainOf(Object startNode){
+		final LinkedList<Object> reverseChain = new LinkedList<Object>();
+		
+		streamParentChainOf(startNode)
+		.forEach(reverseChain::addFirst);
+		
+		return reverseChain.stream();
+	}
+	
+	
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/SimpleParentChain.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/SimpleParentChain.java
new file mode 100644
index 0000000..b30e427
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/SimpleParentChain.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.core.metamodel.util.pchain;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.isis.applib.annotation.Parent;
+import org.apache.isis.core.commons.lang.NullSafe;
+import org.apache.isis.core.commons.reflection.Reflect;
+
+class SimpleParentChain implements ParentChain {
+
+	@Override
+	public Object parentOf(Object node) {
+		if(node==null)
+			return null;
+		
+		final Method getter = parentGetterOf(node);
+		if(getter==null)
+			return null;
+		
+		try {
+			return getter.invoke(node, Reflect.emptyObjects);
+		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+			e.printStackTrace();
+		}
+		
+		return null;
+	}
+	
+	protected static Method parentGetterOf(Object node) {
+		return
+		NullSafe.stream(Reflect.getAllDeclaredMethods(node.getClass()))
+		.filter(ParentChain::providesParent)
+		.findFirst()
+		.orElse(findGetterForAnnotatedField(node));
+	}
+	
+	protected static Method findGetterForAnnotatedField(Object node) {
+		return 
+		NullSafe.stream(Reflect.getAllDeclaredFields(node.getClass()))
+		.filter(f->f.isAnnotationPresent(Parent.class))
+		.findFirst()
+		.map(f->getterOf(node, f.getName()))
+		.orElse(null);
+	}
+	
+	private static Method getterOf(Object bean, String propertyName) {
+		try {
+			return Reflect.getGetter(bean, propertyName);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/SoftCache.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/SoftCache.java
new file mode 100644
index 0000000..34c1ff6
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/util/pchain/SoftCache.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.core.metamodel.util.pchain;
+
+import java.lang.ref.SoftReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Implements a caching {@code Map} where objects are stored referenced by 
+ * a unique key, while {@codeMap.Entries} might be garbage collected any time. 
+ * 
+ * @author ahuber@apache.org
+ *
+ * @param <K>
+ * @param <T>
+ */
+class SoftCache<K,T> {
+	
+	private Map<K,SoftReference<T>> data;
+	
+	public SoftCache() {
+		data=newMap();
+	}
+	
+	public SoftCache(Supplier<Map<K,SoftReference<T>>> mapFactory) {
+		data=mapFactory.get();
+	}
+	
+	/**
+	 * Note: might be overridden to use a different map implementation for storage
+	 * @return
+	 */
+	protected Map<K,SoftReference<T>> newMap(){
+		return new HashMap<>();
+	}
+	
+	/**
+	 * Note: call to this method will fool the garbage collector, 
+	 * so that last objects in the entry set will be kept longer, 
+	 * due to latest access  
+	 * @return number of currently usable SoftReferences 
+	 */
+	public int computeSize(){
+		Map<K,SoftReference<T>> keep = newMap();
+		for(Map.Entry<K,SoftReference<T>> entry : data.entrySet()){
+			if(entry.getValue()!=null) keep.put(entry.getKey(),entry.getValue()); 
+		}
+		data.clear();
+		data=keep;
+		return data.size();
+	}
+	
+	// keep private! (result is not guaranteed to be accurate, 
+	// since the garbage collector may change the soft references any time)
+	@SuppressWarnings("unused")
+	private boolean contains(K key){
+		return get(key)!=null;
+	}
+	
+	public void put(K key, T x){
+		data.put(key, new SoftReference<T>(x));
+	}
+	
+	public T get(K key){
+		SoftReference<T> ref = data.get(key); 
+		if(ref==null) {
+			data.remove(key);
+			return null;
+		}
+		return ref.get();
+	}
+
+	public void clear() {
+		data.clear();		
+	}
+
+	/**
+	 * Tries to fetch a value from cache and returns it if it's a hit. 
+	 * Otherwise stores and returns the value supplied by the mappingFunction.
+	 * @param key
+	 * @param mappingFunction
+	 * @return either the value stored under key or (if there is no such key) the result from the factory 
+	 */
+	public T computeIfAbsent(K key, Function<? super K,? extends T> mappingFunction){
+		return computeIfAbsent(key,()->mappingFunction.apply(key));
+	}
+	
+	/**
+	 * Tries to fetch a value from cache and returns it if it's a hit. 
+	 * Otherwise stores and returns the value supplied by the factory.
+	 * @param key
+	 * @param factory
+	 * @return either the value stored under key or (if there is no such key) the result from the factory 
+	 */
+	public T computeIfAbsent(K key, Supplier<T> factory) {
+		T res = get(key);
+		if(res!=null)
+			return res;
+		res = factory.get();
+		put(key,res); 
+		return res;
+	}
+	
+}
+
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/whereami/WhereAmIModel.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/whereami/WhereAmIModel.java
new file mode 100644
index 0000000..7ecfba0
--- /dev/null
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/whereami/WhereAmIModel.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.viewer.wicket.model.models.whereami;
+
+import java.util.stream.Stream;
+
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
+
+public interface WhereAmIModel {
+
+	public static WhereAmIModel of(EntityModel endOfChain) {
+		return new WhereAmIModelDefault(endOfChain);
+	}
+
+	public boolean isShowWhereAmI();
+	
+	public Stream<EntityModel> streamParentChain();
+	
+	public EntityModel getEndOfChain();
+	
+}
diff --git a/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/whereami/WhereAmIModelDefault.java b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/whereami/WhereAmIModelDefault.java
new file mode 100644
index 0000000..b3f6679
--- /dev/null
+++ b/core/viewer-wicket-model/src/main/java/org/apache/isis/viewer/wicket/model/models/whereami/WhereAmIModelDefault.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.isis.viewer.wicket.model.models.whereami;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.apache.isis.core.metamodel.util.pchain.ParentChain;
+import org.apache.isis.viewer.wicket.model.models.EntityModel;
+
+class WhereAmIModelDefault implements WhereAmIModel {
+
+	private final List<Object> chainOfParents = new ArrayList<>();
+	private final EntityModel endOfChain;
+	
+	public WhereAmIModelDefault(EntityModel endOfChain) {
+		this.endOfChain = endOfChain;
+		
+		final Object startPojo = endOfChain.getObject().getObject();
+
+		ParentChain.caching()
+		.streamReversedParentChainOf(startPojo)
+		.forEach(chainOfParents::add);
+	}
+	
+	@Override
+	public EntityModel getEndOfChain() {
+		return endOfChain;
+	}
+	
+	@Override
+	public boolean isShowWhereAmI() {
+		return !chainOfParents.isEmpty();
+	}
+
+	@Override
+	public Stream<EntityModel> streamParentChain() {
+		return chainOfParents.stream()
+		.map(this::toEntityModel);
+	}
+	
+	// -- HELPER
+
+	private EntityModel toEntityModel(Object domainObject) {
+		return new EntityModel(
+				endOfChain.getPersistenceSession()
+				.adapterFor(domainObject)	);
+	}
+	
+}
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.css b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.css
index eaeea17..b3618df 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.css
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.css
@@ -16,3 +16,32 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
+ 
+ /* === whereAmI feature === */
+
+.whereAmI {
+    color: #555;
+}
+.whereAmI img {
+    width: 16px !important;
+    height: 16px !important;
+}
+ul.whereAmI {
+    padding: 8px 12px;
+    list-style: none;
+    background-color: #eee;
+}
+
+/* Display list items side by side */
+ul.whereAmI li {
+    display: inline;
+}
+
+/* Add a slash symbol (/) before/behind each list item */
+ul.whereAmI li+li:before {
+    padding: 4px;
+    color: black;
+    content: "/\00a0";
+}
+
+/* -------------------------- */
\ No newline at end of file
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.html b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.html
index 1caad01..39aa437 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.html
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.html
@@ -26,8 +26,13 @@
         <wicket:extend>
             <div class="entityPage" wicket:id="entityPageContainer">
                 <div wicket:id="bookmarks"></div>
+                 <div wicket:id="whereAmI-container">
+	                <ul class="whereAmI">
+	    				<li wicket:id="whereAmI-items"></li>
+					</ul>
+                </div>
                 <div wicket:id="entity"></div>
             </div>
         </wicket:extend>
     </body>
-</html>
+</html>
\ No newline at end of file
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.java
index 75e0e5a..2055c4a 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/entity/EntityPage.java
@@ -19,15 +19,6 @@
 
 package org.apache.isis.viewer.wicket.ui.pages.entity;
 
-import org.apache.wicket.Application;
-import org.apache.wicket.RestartResponseException;
-import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-import org.apache.wicket.util.string.Strings;
-
-import org.apache.isis.applib.layout.component.Grid;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.version.ConcurrencyException;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
@@ -39,11 +30,25 @@ import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
 import org.apache.isis.viewer.wicket.model.common.PageParametersUtils;
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.model.models.whereami.WhereAmIModel;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
+import org.apache.isis.viewer.wicket.ui.components.entity.icontitle.EntityIconAndTitlePanel;
 import org.apache.isis.viewer.wicket.ui.components.widgets.breadcrumbs.BreadcrumbModel;
 import org.apache.isis.viewer.wicket.ui.components.widgets.breadcrumbs.BreadcrumbModelProvider;
 import org.apache.isis.viewer.wicket.ui.pages.PageAbstract;
 import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
+import org.apache.wicket.Application;
+import org.apache.wicket.RestartResponseException;
+import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
+import org.apache.wicket.markup.head.CssHeaderItem;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.repeater.RepeatingView;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.request.resource.CssResourceReference;
+import org.apache.wicket.util.string.Strings;
 
 /**
  * Web page representing an entity.
@@ -51,8 +56,10 @@ import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
 @AuthorizeInstantiation("org.apache.isis.viewer.wicket.roles.USER")
 public class EntityPage extends PageAbstract {
 
-    private static final long serialVersionUID = 1L;
-    
+	private static final long serialVersionUID = 144368606134796079L;
+	private static final CssResourceReference WHERE_AM_I_CSS = 
+			new CssResourceReference(EntityPage.class, "EntityPage.css");
+
     private final EntityModel model;
     private final String titleString;
 
@@ -63,6 +70,12 @@ public class EntityPage extends PageAbstract {
     public EntityPage(final PageParameters pageParameters) {
         this(pageParameters, createEntityModel(pageParameters));
     }
+    
+    @Override
+    public void renderHead(IHeaderResponse response) {
+    	super.renderHead(response);
+    	response.render(CssHeaderItem.forReference(WHERE_AM_I_CSS));
+    }
 
     /**
      * Creates an EntityModel from the given page parameters.
@@ -159,7 +172,7 @@ public class EntityPage extends PageAbstract {
             // the facet should always exist, in fact
             // just enough to ask for the metadata.
             // This will cause the current ObjectSpec to be updated as a side effect.
-            final Grid unused = gridFacet.getGrid();
+            gridFacet.getGrid();
         }
 
         if(titleString == null) {
@@ -179,6 +192,8 @@ public class EntityPage extends PageAbstract {
 
         themeDiv.addOrReplace(entityPageContainer);
 
+        addWhereAmIIfShown(entityPageContainer, WhereAmIModel.of(model));
+        
         addChildComponents(entityPageContainer, model);
 
         // bookmarks and breadcrumbs
@@ -186,10 +201,37 @@ public class EntityPage extends PageAbstract {
         addBreadcrumbIfShown(model);
 
         addBookmarkedPages(entityPageContainer);
+        
+        
     }
 
     protected DeploymentCategory getDeploymentCategory() {
         return getIsisSessionFactory().getDeploymentCategory();
     }
-
+    
+    protected void addWhereAmIIfShown(
+    		WebMarkupContainer entityPageContainer, 
+    		WhereAmIModel whereAmIModel) 
+    {
+    	
+    	final WebMarkupContainer whereAmIContainer = 
+    			new WebMarkupContainer("whereAmI-container");
+    	entityPageContainer.addOrReplace(whereAmIContainer);
+    	
+    	if(!whereAmIModel.isShowWhereAmI()) {
+    		whereAmIContainer.setVisible(false);
+    		return;
+    	}
+    	
+    	final RepeatingView listItems = new RepeatingView("whereAmI-items");
+    	
+    	whereAmIModel.streamParentChain().forEach(entityModel->
+    		listItems.add(new EntityIconAndTitlePanel(listItems.newChildId(), entityModel))	
+		);
+    	
+    	listItems.add(new Label(listItems.newChildId(), whereAmIModel.getEndOfChain().getTitle()));
+    	
+    	whereAmIContainer.addOrReplace(listItems);
+    	
+	}
 }

-- 
To stop receiving notification emails like this one, please contact
"commits@isis.apache.org" <co...@isis.apache.org>.