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 2019/01/11 12:25:18 UTC

[isis] 02/05: ISIS-1280: adds Property#projecting attribute and updates wicket UI to use for redirection.

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

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 4b31ce259885840229f71027f839d510f171bca7
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Jan 11 12:06:21 2019 +0000

    ISIS-1280: adds Property#projecting attribute and updates wicket UI to use for redirection.
    
    Also updates the documentation.
---
 .../asciidoc/guides/rgant/_rgant-Property.adoc     |  8 +++
 .../guides/rgant/_rgant-Property_projecting.adoc   | 39 ++++++++++++++
 .../apache/isis/applib/annotation/Projecting.java  | 31 +++++++++++
 .../apache/isis/applib/annotation/Property.java    | 15 ++++++
 .../properties/projection/ProjectionFacet.java     | 29 ++++++++++
 .../projection/ProjectionFacetAbstract.java        | 41 ++++++++++++++
 .../ProjectionFacetFromPropertyAnnotation.java     | 62 ++++++++++++++++++++++
 .../property/PropertyAnnotationFacetFactory.java   | 17 ++++++
 .../entity/icontitle/EntityIconAndTitlePanel.java  | 31 ++++++++++-
 9 files changed, 272 insertions(+), 1 deletion(-)

diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property.adoc
index 1dd8755..fc2f7e7 100644
--- a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property.adoc
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property.adoc
@@ -104,6 +104,13 @@ In many/most cases you should however use xref:../rgant/rgant.adoc#_rgant-Column
 
 
 
+|xref:../rgant/rgant.adoc#_rgant-Property_projecting[`projecting()`]
+|
+|indicates that owning object is a view model that is acting as a projection of the underlying entity referenced by this property.
+
+
+
+
 |xref:../rgant/rgant.adoc#_rgant-Property_regexPattern[`regexPattern()`]
 |regular expression
 |validates the contents of a string parameter against the regular expression pattern
@@ -164,6 +171,7 @@ include::_rgant-Property_maxLength.adoc[leveloffset=+1]
 include::_rgant-Property_mustSatisfy.adoc[leveloffset=+1]
 include::_rgant-Property_notPersisted.adoc[leveloffset=+1]
 include::_rgant-Property_optionality.adoc[leveloffset=+1]
+include::_rgant-Property_projecting.adoc[leveloffset=+1]
 include::_rgant-Property_regexPattern.adoc[leveloffset=+1]
 
 
diff --git a/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property_projecting.adoc b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property_projecting.adoc
new file mode 100644
index 0000000..1a4ffc4
--- /dev/null
+++ b/adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property_projecting.adoc
@@ -0,0 +1,39 @@
+[[_rgant-Property_projecting]]
+= `projecting()`
+:Notice: 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 ag [...]
+:_basedir: ../../
+:_imagesdir: images/
+
+
+A common use case for view models is to act as a projection of some underlying entity, to decorate that entity with additional behaviour (or remove existing behaviour) for some particular business context.
+
+Very often the object that is of interest to the end-user is the underlying entity, not the view model per se.
+If the view model is displayed within a table (on a home page, say), then when the user clicks the entity/icon link for the view model, they will in fact want to drill-down to the underlying entity and skip the view model completely.
+
+The `projecting()` attribute allows the view model to indicate which of its properties holds a reference to the underlying entity of which it is a projection.
+
+For example:
+
+[source,java]
+----
+@ViewModel
+public InvoiceSummary {
+
+    @Property(
+        projecting = Projecting.PROJECTED   // <1>
+        hidden = Where.EVERYWHERE           // <2>
+    }
+    @Getter @Setter
+    private Invoice invoice;
+
+    public LocalDate getDueDate() {         // <3>
+        return invoice.getDueDate();
+    }
+
+    ...
+}
+----
+<1> indicates that this property holds a reference to the underlying entity
+<2> the underlying entity is typically (though not necessarily) hidden
+<3> typical implementation of the properties of the underlying entity that are being projected in the view model.
+
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Projecting.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Projecting.java
new file mode 100644
index 0000000..ce231ce
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Projecting.java
@@ -0,0 +1,31 @@
+/*
+ *  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;
+
+public enum Projecting {
+
+    /**
+     * The property holds the underlying domain object of which this (view model) object is a projection.
+     */
+    PROJECTED,
+    /**
+     * The property
+     */
+    NOT_SPECIFIED;
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java b/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
index 7d248d5..98ad773 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
@@ -75,6 +75,21 @@ public @interface Property {
      */
     Where hidden() default Where.NOWHERE;
 
+    /**
+     * If set to {@link Projecting#PROJECTED projected}, then indicates that the owner of this property is a view model
+     * which is a projection of some other entity, and that the property holds a reference to that
+     * &quot;underlying&quot;.
+     *
+     * <p>
+     *     This is used to automatically redirect any bookmarks to the view model (projection) to instead be directed
+     *     at the underlying entity.
+     * </p>
+     *
+     * <p>
+     *     Only one such property should be marked as being a projection with a view model.
+     * </p>
+     */
+    Projecting projecting() default Projecting.NOT_SPECIFIED;
 
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacet.java
new file mode 100644
index 0000000..a7227ee
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacet.java
@@ -0,0 +1,29 @@
+/*
+ *  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.facets.properties.projection;
+
+import org.apache.isis.applib.annotation.Projecting;
+import org.apache.isis.core.metamodel.facets.SingleValueFacet;
+
+public interface ProjectionFacet extends SingleValueFacet<Projecting> {
+
+    Projecting value();
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacetAbstract.java
new file mode 100644
index 0000000..00a4c7c
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacetAbstract.java
@@ -0,0 +1,41 @@
+/*
+ *  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.facets.properties.projection;
+
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetAbstract;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+
+public abstract class ProjectionFacetAbstract extends FacetAbstract
+        implements ProjectionFacet {
+
+    public static Class<? extends Facet> type() {
+        return ProjectionFacet.class;
+    }
+
+    protected ProjectionFacetAbstract(final FacetHolder holder) {
+        this( holder, Derivation.NOT_DERIVED);
+    }
+
+    protected ProjectionFacetAbstract(final FacetHolder holder, final Derivation derivation) {
+        super( type(), holder, derivation);
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacetFromPropertyAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacetFromPropertyAnnotation.java
new file mode 100644
index 0000000..d65f344
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/projection/ProjectionFacetFromPropertyAnnotation.java
@@ -0,0 +1,62 @@
+/*
+ *  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.facets.properties.projection;
+
+import org.apache.isis.applib.annotation.Projecting;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facetapi.FacetHolder;
+import org.apache.isis.core.metamodel.facets.FacetedMethod;
+
+public class ProjectionFacetFromPropertyAnnotation extends ProjectionFacetAbstract {
+
+    public static Class<? extends Facet> type() {
+        return ProjectionFacet.class;
+    }
+    private final Projecting projecting;
+
+    private ProjectionFacetFromPropertyAnnotation(
+            final Projecting projecting,
+            final FacetHolder holder) {
+        super( holder);
+        this.projecting = projecting;
+    }
+
+    public static ProjectionFacet create(final Property property, final FacetedMethod facetHolder) {
+        if(property == null) {
+            return null;
+        }
+        final Projecting projecting = property.projecting();
+        switch (projecting) {
+            case PROJECTED:
+                return new ProjectionFacetFromPropertyAnnotation(projecting, facetHolder);
+            case NOT_SPECIFIED:
+            default:
+                return null;
+        }
+
+    }
+
+    @Override
+    public Projecting value() {
+        return projecting;
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
index d7869dc..1108d76 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java
@@ -57,6 +57,8 @@ import org.apache.isis.core.metamodel.facets.objectvalue.regex.RegExFacet;
 import org.apache.isis.core.metamodel.facets.objectvalue.regex.TitleFacetFormattedByRegex;
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
 import org.apache.isis.core.metamodel.facets.propcoll.notpersisted.NotPersistedFacet;
+import org.apache.isis.core.metamodel.facets.properties.projection.ProjectionFacet;
+import org.apache.isis.core.metamodel.facets.properties.projection.ProjectionFacetFromPropertyAnnotation;
 import org.apache.isis.core.metamodel.facets.properties.property.command.CommandFacetForPropertyAnnotation;
 import org.apache.isis.core.metamodel.facets.properties.property.disabled.DisabledFacetForDisabledAnnotationOnProperty;
 import org.apache.isis.core.metamodel.facets.properties.property.disabled.DisabledFacetForPropertyAnnotation;
@@ -123,6 +125,7 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme
         processHidden(processMethodContext);
         processEditing(processMethodContext);
         processCommand(processMethodContext);
+        processProjecting(processMethodContext);
         processPublishing(processMethodContext);
         processMaxLength(processMethodContext);
         processMustSatisfy(processMethodContext);
@@ -338,6 +341,20 @@ public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract impleme
         FacetUtil.addFacet(commandFacet);
     }
 
+    void processProjecting(final ProcessMethodContext processMethodContext) {
+
+        final Class<?> cls = processMethodContext.getCls();
+        final Method method = processMethodContext.getMethod();
+        final Property property = Annotations.getAnnotation(method, Property.class);
+        final FacetedMethod facetHolder = processMethodContext.getFacetHolder();
+
+        final ProjectionFacet projectionFacet = ProjectionFacetFromPropertyAnnotation
+                .create(property, facetHolder);
+
+        FacetUtil.addFacet(projectionFacet);
+
+    }
+
     void processPublishing(final ProcessMethodContext processMethodContext) {
 
         final Method method = processMethodContext.getMethod();
diff --git a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/icontitle/EntityIconAndTitlePanel.java b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/icontitle/EntityIconAndTitlePanel.java
index 0b5fa81..c51f6b7 100644
--- a/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/icontitle/EntityIconAndTitlePanel.java
+++ b/core/viewer-wicket-ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/icontitle/EntityIconAndTitlePanel.java
@@ -19,8 +19,13 @@
 
 package org.apache.isis.viewer.wicket.ui.components.entity.icontitle;
 
+import java.util.List;
 import java.util.concurrent.Callable;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
@@ -31,10 +36,14 @@ import org.apache.wicket.markup.html.link.AbstractLink;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.resource.ResourceReference;
 
+import org.apache.isis.applib.annotation.Projecting;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
 import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager.ConcurrencyChecking;
 import org.apache.isis.core.metamodel.facets.members.cssclassfa.CssClassFaFacet;
+import org.apache.isis.core.metamodel.facets.properties.projection.ProjectionFacet;
+import org.apache.isis.core.metamodel.spec.feature.Contributed;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
 import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings;
 import org.apache.isis.viewer.wicket.model.mementos.ObjectAdapterMemento;
@@ -155,6 +164,26 @@ public class EntityIconAndTitlePanel extends PanelAbstract<ObjectAdapterModel> {
             @Override
             public void onClick(final AjaxRequestTarget ajaxRequestTarget) {
                 final ObjectAdapter targetAdapter = entityModel.getObject();
+                final List<OneToOneAssociation> properties = targetAdapter.getSpecification()
+                        .getProperties(Contributed.EXCLUDED);
+                final Optional<OneToOneAssociation> first = FluentIterable.from(properties)
+                        .filter(new Predicate<OneToOneAssociation>() {
+                            @Override public boolean apply(final OneToOneAssociation oneToOneAssociation) {
+                                final ProjectionFacet projectionFacet = oneToOneAssociation
+                                        .getFacet(ProjectionFacet.class);
+                                return projectionFacet != null && !projectionFacet.isNoop()
+                                        && projectionFacet.value() == Projecting.PROJECTED;
+                            }
+                        })
+                        .first();
+
+                final ObjectAdapter redirectToAdapter;
+                if(first.isPresent()) {
+                    final ObjectAdapter underlyingAdapter = first.get().get(targetAdapter);
+                    redirectToAdapter = underlyingAdapter != null ? underlyingAdapter : targetAdapter;
+                } else {
+                    redirectToAdapter = targetAdapter;
+                }
 
                 final EntityPage entityPage =
 
@@ -165,7 +194,7 @@ public class EntityIconAndTitlePanel extends PanelAbstract<ObjectAdapterModel> {
                         AdapterManager.ConcurrencyChecking.executeWithConcurrencyCheckingDisabled(
                                 new Callable<EntityPage>() {
                                     @Override public EntityPage call() throws Exception {
-                                        return new EntityPage(targetAdapter, null);
+                                        return new EntityPage(redirectToAdapter, null);
                                     }
                                 }
                         );