You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2018/01/05 17:43:44 UTC

[isis] branch ISIS-1740-where-am-i updated (53af9fb -> 1f7e51a)

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

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


 discard 53af9fb  ISIS-1740 refactored method names, added javadoc
 discard 8078675  ISIS-1740 initial commit of prototype
     add d727de1  ISIS-1809: adds new implementations of UrlEncodingService
     add e97fa3c  Merge branch 'ISIS-1809-url-encoding-service' into dev/2.0.0-M1
     new ef528d6  ISIS-1740 initial commit of prototype
     new 1f7e51a  ISIS-1740 refactored method names, added javadoc

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (53af9fb)
            \
             N -- N -- N   refs/heads/ISIS-1740-where-am-i (1f7e51a)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 2 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.


Summary of changes:
 .../UrlEncodingServiceUsingBaseEncoding.java       | 10 +++-
 ...aseEncodingWithSupportForLargeUrlsAbstract.java | 69 ++++++++++++++++++++++
 .../UrlEncodingServiceWithCompressionAbstract.java | 66 +++++++++++++++++++++
 ...codingServiceWithCompression_Abstract_Test.java | 35 +++++++++++
 4 files changed, 179 insertions(+), 1 deletion(-)
 create mode 100644 core/applib/src/main/java/org/apache/isis/applib/services/urlencoding/UrlEncodingServiceUsingBaseEncodingWithSupportForLargeUrlsAbstract.java
 create mode 100644 core/applib/src/main/java/org/apache/isis/applib/services/urlencoding/UrlEncodingServiceWithCompressionAbstract.java
 create mode 100644 core/applib/src/test/java/org/apache/isis/applib/services/urlencoding/UrlEncodingServiceWithCompression_Abstract_Test.java

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

[isis] 02/02: ISIS-1740 refactored method names, added javadoc

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

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

commit 1f7e51aa1c8d7a4ef750cd9a7c133eac7ebfd6c2
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Oct 27 16:03:36 2017 +0200

    ISIS-1740 refactored method names, added javadoc
---
 .../model/models/whereami/WhereAmIModel.java       | 28 ++++++++++++++++++----
 .../models/whereami/WhereAmIModelDefault.java      | 24 +++++++++----------
 .../viewer/wicket/ui/pages/entity/EntityPage.java  |  4 ++--
 3 files changed, 38 insertions(+), 18 deletions(-)

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
index 7ecfba0..fdc11f2 100644
--- 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
@@ -23,16 +23,36 @@ import java.util.stream.Stream;
 
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
 
+/**
+ * Represents a navigable chain of parent nodes starting at the current node. 
+ * 
+ * @author a.huber@corax.at
+ * 
+ * @since 2.0.0
+ *
+ */
 public interface WhereAmIModel {
 
-	public static WhereAmIModel of(EntityModel endOfChain) {
-		return new WhereAmIModelDefault(endOfChain);
+	public static WhereAmIModel of(EntityModel startOfChain) {
+		return new WhereAmIModelDefault(startOfChain);
 	}
 
+	/**
+	 * The navigable parent chain requires a minimum length of 2 in order to be shown.
+	 * @return whether the where-am-I hint should be shown or hidden
+	 */
 	public boolean isShowWhereAmI();
 	
-	public Stream<EntityModel> streamParentChain();
+	/**
+	 * Streams the linked nodes of this model's navigable parent chain in reverse order.
+	 * @return reversed order stream of linked parent nodes, which does not include the start node
+	 */
+	public Stream<EntityModel> streamParentChainReversed();
 	
-	public EntityModel getEndOfChain();
+	/**
+	 *  
+	 * @return the immutable start node of the navigable parent chain
+	 */
+	public EntityModel getStartOfChain();
 	
 }
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
index b3f6679..955c2db 100644
--- 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
@@ -28,32 +28,32 @@ import org.apache.isis.viewer.wicket.model.models.EntityModel;
 
 class WhereAmIModelDefault implements WhereAmIModel {
 
-	private final List<Object> chainOfParents = new ArrayList<>();
-	private final EntityModel endOfChain;
+	private final List<Object> reversedChainOfParents = new ArrayList<>();
+	private final EntityModel startOfChain;
 	
-	public WhereAmIModelDefault(EntityModel endOfChain) {
-		this.endOfChain = endOfChain;
+	public WhereAmIModelDefault(EntityModel startOfChain) {
+		this.startOfChain = startOfChain;
 		
-		final Object startPojo = endOfChain.getObject().getObject();
+		final Object startPojo = startOfChain.getObject().getObject();
 
 		ParentChain.caching()
 		.streamReversedParentChainOf(startPojo)
-		.forEach(chainOfParents::add);
+		.forEach(reversedChainOfParents::add);
 	}
 	
 	@Override
-	public EntityModel getEndOfChain() {
-		return endOfChain;
+	public EntityModel getStartOfChain() {
+		return startOfChain;
 	}
 	
 	@Override
 	public boolean isShowWhereAmI() {
-		return !chainOfParents.isEmpty();
+		return !reversedChainOfParents.isEmpty();
 	}
 
 	@Override
-	public Stream<EntityModel> streamParentChain() {
-		return chainOfParents.stream()
+	public Stream<EntityModel> streamParentChainReversed() {
+		return reversedChainOfParents.stream()
 		.map(this::toEntityModel);
 	}
 	
@@ -61,7 +61,7 @@ class WhereAmIModelDefault implements WhereAmIModel {
 
 	private EntityModel toEntityModel(Object domainObject) {
 		return new EntityModel(
-				endOfChain.getPersistenceSession()
+				startOfChain.getPersistenceSession()
 				.adapterFor(domainObject)	);
 	}
 	
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 e9e04dc..d105c48 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
@@ -226,11 +226,11 @@ public class EntityPage extends PageAbstract {
     	
     	final RepeatingView listItems = new RepeatingView("whereAmI-items");
     	
-    	whereAmIModel.streamParentChain().forEach(entityModel->
+    	whereAmIModel.streamParentChainReversed().forEach(entityModel->
     		listItems.add(new EntityIconAndTitlePanel(listItems.newChildId(), entityModel))	
 		);
     	
-    	listItems.add(new Label(listItems.newChildId(), whereAmIModel.getEndOfChain().getTitle()));
+    	listItems.add(new Label(listItems.newChildId(), whereAmIModel.getStartOfChain().getTitle()));
     	
     	whereAmIContainer.addOrReplace(listItems);
     	

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

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

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

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

commit ef528d66a5183e6c8076fdfa1688278fec0b284f
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  |  53 ++++++++-
 10 files changed, 587 insertions(+), 6 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 f824e1d..e9e04dc 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
@@ -22,12 +22,16 @@ 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.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;
 
-import org.apache.isis.applib.layout.grid.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,7 +43,9 @@ 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;
@@ -51,8 +57,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 +71,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 +173,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 +193,8 @@ public class EntityPage extends PageAbstract {
 
         themeDiv.addOrReplace(entityPageContainer);
 
+        addWhereAmIIfShown(entityPageContainer, WhereAmIModel.of(model));
+        
         addChildComponents(entityPageContainer, model);
 
         // bookmarks and breadcrumbs
@@ -186,10 +202,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>.