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 2012/12/06 12:09:57 UTC
[35/51] [partial] ISIS-188: moving components into correct
directories.
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-applib/src/main/java/org/apache/isis/progmodel/wrapper/applib/listeners/InteractionListener.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-applib/src/main/java/org/apache/isis/progmodel/wrapper/applib/listeners/InteractionListener.java b/framework/progmodel/wrapper/wrapper-applib/src/main/java/org/apache/isis/progmodel/wrapper/applib/listeners/InteractionListener.java
new file mode 100644
index 0000000..58a1228
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-applib/src/main/java/org/apache/isis/progmodel/wrapper/applib/listeners/InteractionListener.java
@@ -0,0 +1,194 @@
+/*
+ * 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.progmodel.wrapper.applib.listeners;
+
+import org.apache.isis.applib.events.ActionArgumentEvent;
+import org.apache.isis.applib.events.ActionInvocationEvent;
+import org.apache.isis.applib.events.ActionUsabilityEvent;
+import org.apache.isis.applib.events.ActionVisibilityEvent;
+import org.apache.isis.applib.events.CollectionAccessEvent;
+import org.apache.isis.applib.events.CollectionAddToEvent;
+import org.apache.isis.applib.events.CollectionMethodEvent;
+import org.apache.isis.applib.events.CollectionRemoveFromEvent;
+import org.apache.isis.applib.events.CollectionUsabilityEvent;
+import org.apache.isis.applib.events.CollectionVisibilityEvent;
+import org.apache.isis.applib.events.ObjectTitleEvent;
+import org.apache.isis.applib.events.ObjectValidityEvent;
+import org.apache.isis.applib.events.PropertyAccessEvent;
+import org.apache.isis.applib.events.PropertyModifyEvent;
+import org.apache.isis.applib.events.PropertyUsabilityEvent;
+import org.apache.isis.applib.events.PropertyVisibilityEvent;
+
+public interface InteractionListener {
+
+ /**
+ * The title was read.
+ *
+ * @param ev
+ */
+ void objectTitleRead(ObjectTitleEvent ev);
+
+ /**
+ * The object was persisted (or an attempt to persist it was made).
+ *
+ * @param ev
+ */
+ void objectPersisted(ObjectValidityEvent ev);
+
+ /**
+ * A check was made to determine if a property was visible.
+ *
+ * @param ev
+ */
+ void propertyVisible(PropertyVisibilityEvent ev);
+
+ /**
+ * A check was made to determine if a property was usable.
+ *
+ * @param ev
+ */
+ void propertyUsable(PropertyUsabilityEvent ev);
+
+ /**
+ * A property was read.
+ *
+ * <p>
+ * Unlike most other events, a {@link PropertyAccessEvent} will never have
+ * been vetoed (that is, {@link PropertyAccessEvent#isVeto()} will always be
+ * <tt>false</tt>).
+ *
+ * @param ev
+ */
+ void propertyAccessed(PropertyAccessEvent ev);
+
+ /**
+ * A property was modified (or an attempt to modify it was made)
+ *
+ * <p>
+ * Use {@link PropertyModifyEvent#getProposed()} to determine whether the
+ * property was being set or cleared.
+ *
+ * @param ev
+ */
+ void propertyModified(PropertyModifyEvent ev);
+
+ /**
+ * A check was made to determine if a collection was visible.
+ *
+ * <p>
+ * Will be fired prior to
+ * {@link #collectionUsable(CollectionUsabilityEvent)}.
+ *
+ * @param ev
+ */
+ void collectionVisible(CollectionVisibilityEvent ev);
+
+ /**
+ * A check was made to determine if a collection was usable.
+ *
+ * <p>
+ * Will be fired prior to either
+ * {@link #collectionAccessed(CollectionAccessEvent)} or
+ * {@link #collectionAddedTo(CollectionAddToEvent)} or
+ * {@link #collectionRemovedFrom(CollectionRemoveFromEvent)}.
+ *
+ * @param ev
+ */
+ void collectionUsable(CollectionUsabilityEvent ev);
+
+ /**
+ * A collection was read.
+ *
+ * <p>
+ * Unlike most other events, a {@link CollectionAccessEvent} will never have
+ * been vetoed (that is, {@link CollectionAccessEvent#isVeto()} will always
+ * be <tt>false</tt>).
+ *
+ * @param ev
+ */
+ void collectionAccessed(CollectionAccessEvent ev);
+
+ /**
+ * An object was added to the collection (or an attempt to add it was made).
+ *
+ * @param ev
+ */
+ void collectionAddedTo(CollectionAddToEvent ev);
+
+ /**
+ * An object was removed from the collection (or an attempt to remove it was
+ * made).
+ *
+ * @param ev
+ */
+ void collectionRemovedFrom(CollectionRemoveFromEvent ev);
+
+/**
+ * A method of a collection (such as <tt>isEmpty()</tt> or <tt>size()</tt>) has been invoked.
+ *
+ *
+ * <p>
+ * Unlike the other methods in this interface, the source of these events will be an instance of a
+ * Collection (such as <tt>java.util.List</tt>) rather than the domain object. (The domain object is
+ * {@link CollectionMethodEvent#getDomainObject() still available, however).
+ *
+ * @param interactionEvent
+ */
+ void collectionMethodInvoked(CollectionMethodEvent interactionEvent);
+
+ /**
+ * A check was made to determine if an action was visible.
+ *
+ * <p>
+ * Will be fired prior to {@link #actionUsable(ActionUsabilityEvent)}.
+ *
+ * @param ev
+ */
+ void actionVisible(ActionVisibilityEvent interactionEvent);
+
+ /**
+ * A check was made to determine if an action was usable.
+ *
+ * <p>
+ * Will be fired prior to {@link #actionArgument(ActionArgumentEvent)}.
+ *
+ * @param ev
+ */
+ void actionUsable(ActionUsabilityEvent ev);
+
+ /**
+ * A check was made as to whether an argument proposed for an action was
+ * valid.
+ *
+ * <p>
+ * Will be fired prior to {@link #actionInvoked(ActionInvocationEvent)}.
+ *
+ * @param ev
+ */
+ void actionArgument(ActionArgumentEvent ev);
+
+ /**
+ * An action was invoked (or an attempt to invoke it was made).
+ *
+ * @param ev
+ */
+ void actionInvoked(ActionInvocationEvent ev);
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-applib/src/site/apt/index.apt
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-applib/src/site/apt/index.apt b/framework/progmodel/wrapper/wrapper-applib/src/site/apt/index.apt
new file mode 100644
index 0000000..ae48c91
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-applib/src/site/apt/index.apt
@@ -0,0 +1,24 @@
+~~ 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.
+
+
+
+Headless AppLib
+
+ The <headless applib> decouples the domain objects from the <headless> viewer implementation. Its main
+ responsibility is to define the <<<HeadlessViewer>>> interface, by which the domain object proxies are obtained.
+ It also defines the exceptions that are raised to indicate invalid interactions between domain objects.
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-applib/src/site/apt/jottings.apt
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-applib/src/site/apt/jottings.apt b/framework/progmodel/wrapper/wrapper-applib/src/site/apt/jottings.apt
new file mode 100644
index 0000000..c5d1200
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-applib/src/site/apt/jottings.apt
@@ -0,0 +1,24 @@
+~~ 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.
+
+
+
+Jottings
+
+ This page is to capture any random jottings relating to this module prior
+ to being moved into formal documentation.
+
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-applib/src/site/site.xml
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-applib/src/site/site.xml b/framework/progmodel/wrapper/wrapper-applib/src/site/site.xml
new file mode 100644
index 0000000..692a6a3
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-applib/src/site/site.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project>
+
+ <body>
+ <breadcrumbs>
+ <item name="Applib" href="index.html"/>
+ </breadcrumbs>
+
+ <menu name="Wrapper Applib">
+ <item name="About" href="index.html" />
+ <item name="Jottings" href="jottings.html" />
+ </menu>
+
+ <menu name="Wrapper Modules">
+ <item name="Applib" href="../wrapper-applib/index.html" />
+ <item name="Metamodel" href="../wrapper-metamodel/index.html" />
+ </menu>
+
+ <menu name="Maven Reports" ref="reports" />
+ </body>
+</project>
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/NOTICE
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/NOTICE b/framework/progmodel/wrapper/wrapper-metamodel/NOTICE
new file mode 100644
index 0000000..d391f54
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/NOTICE
@@ -0,0 +1,7 @@
+Apache Isis
+Copyright 2010-2011 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/pom.xml
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/pom.xml b/framework/progmodel/wrapper/wrapper-metamodel/pom.xml
new file mode 100644
index 0000000..d8e633c
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/pom.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.isis.progmodels</groupId>
+ <artifactId>wrapper</artifactId>
+ <version>0.3.1-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>wrapper-metamodel</artifactId>
+
+ <name>Wrapper ProgModel MetaModel</name>
+ <description>Wrapper Metamodel</description>
+
+
+ <properties>
+ <siteBaseDir>../../..</siteBaseDir>
+ <relativeUrl>progmodels/wrapper/wrapper-metamodel/</relativeUrl>
+ </properties>
+
+ <!-- used in Site generation for relative references. -->
+ <url>http://incubator.apache.org/isis/${relativeUrl}</url>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <version>${maven-project-info-reports-plugin}</version>
+ <inherited>false</inherited>
+ <configuration>
+ <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
+ </configuration>
+ <reportSets>
+ <reportSet>
+ <inherited>false</inherited>
+ <reports>
+ <report>dependencies</report>
+ <report>dependency-convergence</report>
+ <report>plugins</report>
+ <report>summary</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ </plugins>
+ </reporting>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.isis.progmodels</groupId>
+ <artifactId>wrapper-applib</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.runtimes.dflt</groupId>
+ <artifactId>runtime</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.core</groupId>
+ <artifactId>isis-metamodel</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.isis.runtimes.dflt</groupId>
+ <artifactId>runtime</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.tck</groupId>
+ <artifactId>tck-dom</artifactId>
+ <scope>test</scope>
+ <version>0.3.1-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>asm</groupId>
+ <artifactId>asm</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.objenesis</groupId>
+ <artifactId>objenesis</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib-nodep</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.isis.core</groupId>
+ <artifactId>isis-unittestsupport</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/DomainObjectContainerWrapperFactory.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/DomainObjectContainerWrapperFactory.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/DomainObjectContainerWrapperFactory.java
new file mode 100644
index 0000000..a5f09e8
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/DomainObjectContainerWrapperFactory.java
@@ -0,0 +1,116 @@
+/*
+ * 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.progmodel.wrapper.metamodel;
+
+import java.util.List;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.events.InteractionEvent;
+import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider;
+import org.apache.isis.core.metamodel.adapter.ObjectPersistor;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.services.container.DomainObjectContainerDefault;
+import org.apache.isis.core.metamodel.spec.SpecificationLoader;
+import org.apache.isis.progmodel.wrapper.applib.WrapperFactory;
+import org.apache.isis.progmodel.wrapper.applib.listeners.InteractionListener;
+import org.apache.isis.progmodel.wrapper.metamodel.internal.WrapperFactoryDefault;
+
+/**
+ * A combined {@link DomainObjectContainer} and {@link WrapperFactory}.
+ */
+public class DomainObjectContainerWrapperFactory extends DomainObjectContainerDefault implements WrapperFactory {
+
+ private final WrapperFactoryDefault wrapperFactoryDelegate;
+
+ public DomainObjectContainerWrapperFactory() {
+ this.wrapperFactoryDelegate = new WrapperFactoryDefault();
+ }
+
+ // /////////////////////////////////////////////////////////////
+ // Views
+ // /////////////////////////////////////////////////////////////
+
+ @Override
+ public <T> T wrap(final T domainObject) {
+ return wrapperFactoryDelegate.wrap(domainObject);
+ }
+
+ @Override
+ public <T> T wrap(final T domainObject, final ExecutionMode mode) {
+ return wrapperFactoryDelegate.wrap(domainObject, mode);
+ }
+
+ @Override
+ public boolean isWrapper(final Object possibleView) {
+ return wrapperFactoryDelegate.isWrapper(possibleView);
+ }
+
+ // /////////////////////////////////////////////////////////////
+ // Listeners
+ // /////////////////////////////////////////////////////////////
+
+ @Override
+ public List<InteractionListener> getListeners() {
+ return wrapperFactoryDelegate.getListeners();
+ }
+
+ @Override
+ public boolean addInteractionListener(final InteractionListener listener) {
+ return wrapperFactoryDelegate.addInteractionListener(listener);
+ }
+
+ @Override
+ public boolean removeInteractionListener(final InteractionListener listener) {
+ return wrapperFactoryDelegate.removeInteractionListener(listener);
+ }
+
+ @Override
+ public void notifyListeners(final InteractionEvent interactionEvent) {
+ wrapperFactoryDelegate.notifyListeners(interactionEvent);
+ }
+
+ // /////////////////////////////////////////////////////////////
+ // Dependencies
+ // /////////////////////////////////////////////////////////////
+
+ @Override
+ public void setSpecificationLookup(final SpecificationLoader specificationLookup) {
+ super.setSpecificationLookup(specificationLookup);
+ wrapperFactoryDelegate.setSpecificationLookup(specificationLookup);
+ }
+
+ @Override
+ public void setAuthenticationSessionProvider(final AuthenticationSessionProvider authenticationSessionProvider) {
+ super.setAuthenticationSessionProvider(authenticationSessionProvider);
+ wrapperFactoryDelegate.setAuthenticationSessionProvider(authenticationSessionProvider);
+ }
+
+ @Override
+ public void setAdapterManager(final AdapterManager adapterManager) {
+ super.setAdapterManager(adapterManager);
+ wrapperFactoryDelegate.setAdapterManager(adapterManager);
+ }
+
+ @Override
+ public void setObjectPersistor(final ObjectPersistor objectPersistor) {
+ super.setObjectPersistor(objectPersistor);
+ wrapperFactoryDelegate.setObjectPersistor(objectPersistor);
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/AbstractCollectionInvocationHandler.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/AbstractCollectionInvocationHandler.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/AbstractCollectionInvocationHandler.java
new file mode 100644
index 0000000..75e5fce
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/AbstractCollectionInvocationHandler.java
@@ -0,0 +1,89 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.isis.applib.events.CollectionMethodEvent;
+import org.apache.isis.applib.events.InteractionEvent;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+
+abstract class AbstractCollectionInvocationHandler<T, C> extends DelegatingInvocationHandlerDefault<C> {
+
+ private final List<Method> interceptedMethods = new ArrayList<Method>();
+ private final List<Method> vetoedMethods = new ArrayList<Method>();
+ private final String collectionName;
+ private final OneToManyAssociation oneToManyAssociation;
+ private final T domainObject;
+
+ public AbstractCollectionInvocationHandler(final C collectionOrMapToProxy, final String collectionName, final DomainObjectInvocationHandler<T> handler, final OneToManyAssociation otma) {
+ super(collectionOrMapToProxy, handler.getHeadlessViewer(), handler.getExecutionMode());
+ this.collectionName = collectionName;
+ this.oneToManyAssociation = otma;
+ this.domainObject = handler.getDelegate();
+ }
+
+ protected Method intercept(final Method method) {
+ this.interceptedMethods.add(method);
+ return method;
+ }
+
+ protected Method veto(final Method method) {
+ this.vetoedMethods.add(method);
+ return method;
+ }
+
+ public String getCollectionName() {
+ return collectionName;
+ }
+
+ public OneToManyAssociation getCollection() {
+ return oneToManyAssociation;
+ }
+
+ public T getDomainObject() {
+ return domainObject;
+ }
+
+ @Override
+ public Object invoke(final Object collectionObject, final Method method, final Object[] args) throws Throwable {
+
+ // delegate
+ final Object returnValueObj = delegate(method, args);
+
+ if (interceptedMethods.contains(method)) {
+
+ resolveIfRequired(domainObject);
+
+ final InteractionEvent ev = new CollectionMethodEvent(getDelegate(), getCollection().getIdentifier(), getDomainObject(), method.getName(), args, returnValueObj);
+ notifyListeners(ev);
+ return returnValueObj;
+ }
+
+ if (vetoedMethods.contains(method)) {
+ throw new UnsupportedOperationException(String.format("Method '%s' may not be called directly.", method.getName()));
+ }
+
+ return returnValueObj;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CgLibClassProxyFactory.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CgLibClassProxyFactory.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CgLibClassProxyFactory.java
new file mode 100644
index 0000000..aaa2ae0
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CgLibClassProxyFactory.java
@@ -0,0 +1,71 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import java.lang.reflect.InvocationHandler;
+
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.Factory;
+import net.sf.cglib.proxy.MethodInterceptor;
+
+import org.apache.isis.progmodel.wrapper.applib.WrapperObject;
+
+/**
+ * Factory generating a mock for a class.
+ * <p>
+ * Note that this class is stateful
+ */
+public class CgLibClassProxyFactory<T> implements IProxyFactory<T> {
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T createProxy(final T toProxy, final InvocationHandler handler) {
+ final Class<T> proxyClass = (Class<T>) toProxy.getClass();
+ return createProxy(proxyClass, handler);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T createProxy(final Class<T> toProxyClass, final InvocationHandler handler) {
+
+ final MethodInterceptor interceptor = new InvocationHandlerMethodInterceptor(handler);
+
+ // Create the proxy
+ final Enhancer enhancer = new Enhancer();
+ enhancer.setSuperclass(toProxyClass);
+ enhancer.setInterfaces(new Class[] { WrapperObject.class });
+ enhancer.setCallbackType(interceptor.getClass());
+
+ final Class<?> enhancedClass = enhancer.createClass();
+
+ Enhancer.registerCallbacks(enhancedClass, new Callback[] { interceptor });
+
+ Factory factory;
+ try {
+ factory = (Factory) ClassInstantiatorFactoryCE.getInstantiator().newInstance(enhancedClass);
+ } catch (final InstantiationException e) {
+ throw new RuntimeException("Fail to instantiate mock for " + toProxyClass + " on " + ClassInstantiatorFactoryCE.getJVM() + " JVM");
+ }
+
+ return (T) factory;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CgLibProxy.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CgLibProxy.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CgLibProxy.java
new file mode 100644
index 0000000..2d2cec8
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CgLibProxy.java
@@ -0,0 +1,73 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.Factory;
+
+import org.apache.isis.core.metamodel.specloader.classsubstitutor.CglibEnhanced;
+import org.apache.isis.progmodel.wrapper.applib.WrapperObject;
+
+public class CgLibProxy<T> {
+
+ private final DelegatingInvocationHandler<T> handler;
+
+ public CgLibProxy(final DelegatingInvocationHandler<T> handler) {
+ this.handler = handler;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T proxy() {
+
+ final T toProxy = handler.getDelegate();
+
+ // handle if already proxied using cglib.
+ if (CglibEnhanced.class.isAssignableFrom(toProxy.getClass())) {
+
+ handler.setResolveObjectChangedEnabled(true);
+
+ final Class<? extends Object> enhancedClass = toProxy.getClass();
+ final Class<? extends Object> origSuperclass = toProxy.getClass().getSuperclass();
+
+ final List<Class> interfaces = new ArrayList<Class>();
+ interfaces.addAll(Arrays.asList(enhancedClass.getInterfaces()));
+ interfaces.remove(Factory.class); // if there.
+ interfaces.add(WrapperObject.class);
+
+ return (T) Enhancer.create(origSuperclass, interfaces.toArray(new Class[] {}), new InvocationHandlerMethodInterceptor(handler));
+ }
+
+ final Class<T> clazz = (Class<T>) toProxy.getClass();
+
+ T proxy = null;
+ try {
+ final IProxyFactory<T> proxyFactory = clazz.isInterface() ? new JavaProxyFactory<T>() : new CgLibClassProxyFactory<T>();
+ proxy = proxyFactory.createProxy(clazz, handler);
+ } catch (final RuntimeExceptionWrapper e) {
+ throw (RuntimeException) e.getRuntimeException().fillInStackTrace();
+ }
+ return proxy;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/ClassInstantiatorFactoryCE.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/ClassInstantiatorFactoryCE.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/ClassInstantiatorFactoryCE.java
new file mode 100644
index 0000000..85bc196
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/ClassInstantiatorFactoryCE.java
@@ -0,0 +1,65 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+/**
+ * Factory returning a {@link IClassInstantiatorCE}for the current JVM
+ */
+class ClassInstantiatorFactoryCE {
+
+ private static IClassInstantiatorCE instantiator = new ObjenesisClassInstantiatorCE();
+
+ // ///CLOVER:OFF
+ private ClassInstantiatorFactoryCE() {
+ }
+
+ // ///CLOVER:ON
+
+ /**
+ * Returns the current JVM as specified in the Systtem properties
+ *
+ * @return current JVM
+ */
+ public static String getJVM() {
+ return System.getProperty("java.vm.vendor");
+ }
+
+ /**
+ * Returns the current JVM specification version (1.5, 1.4, 1.3)
+ *
+ * @return current JVM specification version
+ */
+ public static String getJVMSpecificationVersion() {
+ return System.getProperty("java.specification.version");
+ }
+
+ public static boolean is1_3Specifications() {
+ return getJVMSpecificationVersion().equals("1.3");
+ }
+
+ /**
+ * Returns a class instantiator suitable for the current JVM
+ *
+ * @return a class instantiator usable on the current JVM
+ */
+ public static IClassInstantiatorCE getInstantiator() {
+ return instantiator;
+ }
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CollectionInvocationHandler.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CollectionInvocationHandler.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CollectionInvocationHandler.java
new file mode 100644
index 0000000..d143af6
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/CollectionInvocationHandler.java
@@ -0,0 +1,54 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import static org.apache.isis.core.commons.lang.MethodUtils.getMethod;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+
+class CollectionInvocationHandler<T, R> extends AbstractCollectionInvocationHandler<T, R> {
+
+ public CollectionInvocationHandler(final R collectionToProxy, final String collectionName, final DomainObjectInvocationHandler<T> handler, final OneToManyAssociation otma) {
+ super(collectionToProxy, collectionName, handler, otma);
+
+ try {
+ intercept(getMethod(collectionToProxy, "contains", Object.class));
+ intercept(getMethod(collectionToProxy, "size"));
+ intercept(getMethod(collectionToProxy, "isEmpty"));
+ if (collectionToProxy instanceof List) {
+ intercept(getMethod(collectionToProxy, "get", int.class));
+ }
+ veto(getMethod(collectionToProxy, "add", Object.class));
+ veto(getMethod(collectionToProxy, "remove", Object.class));
+ veto(getMethod(collectionToProxy, "addAll", Collection.class));
+ veto(getMethod(collectionToProxy, "removeAll", Collection.class));
+ veto(getMethod(collectionToProxy, "retainAll", Collection.class));
+ veto(getMethod(collectionToProxy, "clear"));
+ } catch (final NoSuchMethodException e) {
+ // ///CLOVER:OFF
+ throw new RuntimeException("A Collection method could not be found: " + e.getMessage());
+ // ///CLOVER:ON
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DelegatingInvocationHandler.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DelegatingInvocationHandler.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DelegatingInvocationHandler.java
new file mode 100644
index 0000000..5772b88
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DelegatingInvocationHandler.java
@@ -0,0 +1,32 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import java.lang.reflect.InvocationHandler;
+
+public interface DelegatingInvocationHandler<T> extends InvocationHandler {
+
+ T getDelegate();
+
+ public boolean isResolveObjectChangedEnabled();
+
+ public void setResolveObjectChangedEnabled(boolean resolveObjectChangedEnabled);
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DelegatingInvocationHandlerDefault.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DelegatingInvocationHandlerDefault.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DelegatingInvocationHandlerDefault.java
new file mode 100644
index 0000000..a13d6cb
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DelegatingInvocationHandlerDefault.java
@@ -0,0 +1,131 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.isis.applib.events.InteractionEvent;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.progmodel.wrapper.applib.WrapperFactory;
+import org.apache.isis.progmodel.wrapper.applib.WrapperFactory.ExecutionMode;
+import org.apache.isis.runtimes.dflt.runtime.persistence.container.DomainObjectContainerObjectChanged;
+import org.apache.isis.runtimes.dflt.runtime.persistence.container.DomainObjectContainerResolve;
+
+public class DelegatingInvocationHandlerDefault<T> implements DelegatingInvocationHandler<T> {
+
+ private final T delegate;
+ protected final WrapperFactory wrapperFactory;
+ private final ExecutionMode executionMode;
+
+ protected final Method equalsMethod;
+ protected final Method hashCodeMethod;
+ protected final Method toStringMethod;
+
+ private final DomainObjectContainerObjectChanged domainObjectContainerObjectChanged;
+ private final DomainObjectContainerResolve domainObjectContainerResolve;
+
+ private boolean resolveObjectChangedEnabled;
+
+ public DelegatingInvocationHandlerDefault(final T delegate, final WrapperFactory headlessViewer, final ExecutionMode executionMode) {
+ if (delegate == null) {
+ throw new IllegalArgumentException("delegate must not be null");
+ }
+ this.delegate = delegate;
+ this.wrapperFactory = headlessViewer;
+ this.executionMode = executionMode;
+
+ this.domainObjectContainerResolve = new DomainObjectContainerResolve();
+ this.domainObjectContainerObjectChanged = new DomainObjectContainerObjectChanged();
+
+ try {
+ equalsMethod = delegate.getClass().getMethod("equals", new Class[] { Object.class });
+ hashCodeMethod = delegate.getClass().getMethod("hashCode", new Class[] {});
+ toStringMethod = delegate.getClass().getMethod("toString", new Class[] {});
+ } catch (final NoSuchMethodException e) {
+ // ///CLOVER:OFF
+ throw new RuntimeException("An Object method could not be found: " + e.getMessage());
+ // ///CLOVER:ON
+ }
+ }
+
+ @Override
+ public boolean isResolveObjectChangedEnabled() {
+ return resolveObjectChangedEnabled;
+ }
+
+ @Override
+ public void setResolveObjectChangedEnabled(final boolean resolveObjectChangedEnabled) {
+ this.resolveObjectChangedEnabled = resolveObjectChangedEnabled;
+ }
+
+ protected void resolveIfRequired(final ObjectAdapter targetAdapter) {
+ resolveIfRequired(targetAdapter.getObject());
+ }
+
+ protected void resolveIfRequired(final Object domainObject) {
+ if (resolveObjectChangedEnabled) {
+ domainObjectContainerResolve.resolve(domainObject);
+ }
+ }
+
+ protected void objectChangedIfRequired(final ObjectAdapter targetAdapter) {
+ objectChangedIfRequired(targetAdapter.getObject());
+ }
+
+ protected void objectChangedIfRequired(final Object domainObject) {
+ if (resolveObjectChangedEnabled) {
+ domainObjectContainerObjectChanged.objectChanged(domainObject);
+ }
+ }
+
+ public WrapperFactory getHeadlessViewer() {
+ return wrapperFactory;
+ }
+
+ @Override
+ public T getDelegate() {
+ return delegate;
+ }
+
+ public ExecutionMode getExecutionMode() {
+ return executionMode;
+ }
+
+ protected Object delegate(final Method method, final Object[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+
+ return method.invoke(getDelegate(), args);
+ }
+
+ protected boolean isObjectMethod(final Method method) {
+ return toStringMethod.equals(method) || hashCodeMethod.equals(method) || equalsMethod.equals(method);
+ }
+
+ @Override
+ public Object invoke(final Object object, final Method method, final Object[] args) throws Throwable {
+ return method.invoke(object, args);
+ }
+
+ protected InteractionEvent notifyListeners(final InteractionEvent interactionEvent) {
+ wrapperFactory.notifyListeners(interactionEvent);
+ return interactionEvent;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DomainObjectInvocationHandler.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DomainObjectInvocationHandler.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DomainObjectInvocationHandler.java
new file mode 100644
index 0000000..fbc37f0
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/DomainObjectInvocationHandler.java
@@ -0,0 +1,645 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.events.CollectionAccessEvent;
+import org.apache.isis.applib.events.InteractionEvent;
+import org.apache.isis.applib.events.ObjectTitleEvent;
+import org.apache.isis.applib.events.PropertyAccessEvent;
+import org.apache.isis.applib.events.UsabilityEvent;
+import org.apache.isis.applib.events.ValidityEvent;
+import org.apache.isis.applib.events.VisibilityEvent;
+import org.apache.isis.applib.filter.Filter;
+import org.apache.isis.core.commons.authentication.AuthenticationSession;
+import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
+import org.apache.isis.core.metamodel.adapter.ObjectPersistor;
+import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
+import org.apache.isis.core.metamodel.adapter.util.AdapterUtils;
+import org.apache.isis.core.metamodel.consent.Consent;
+import org.apache.isis.core.metamodel.consent.InteractionInvocationMethod;
+import org.apache.isis.core.metamodel.consent.InteractionResult;
+import org.apache.isis.core.metamodel.facetapi.Facet;
+import org.apache.isis.core.metamodel.facets.ImperativeFacet;
+import org.apache.isis.core.metamodel.facets.accessor.PropertyOrCollectionAccessorFacet;
+import org.apache.isis.core.metamodel.facets.actions.choices.ActionChoicesFacet;
+import org.apache.isis.core.metamodel.facets.actions.defaults.ActionDefaultsFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToFacet;
+import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet;
+import org.apache.isis.core.metamodel.facets.param.choices.ActionParameterChoicesFacet;
+import org.apache.isis.core.metamodel.facets.properties.choices.PropertyChoicesFacet;
+import org.apache.isis.core.metamodel.facets.properties.defaults.PropertyDefaultFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertyInitializationFacet;
+import org.apache.isis.core.metamodel.facets.properties.modify.PropertySetterFacet;
+import org.apache.isis.core.metamodel.interactions.ObjectTitleContext;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.SpecificationLoader;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.isis.core.metamodel.specloader.specimpl.dflt.ObjectSpecificationDefault;
+import org.apache.isis.core.progmodel.facets.actions.validate.method.ActionValidationFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.collections.validate.CollectionValidateAddToFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.collections.validate.CollectionValidateRemoveFromFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.members.disabled.method.DisableForContextFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.members.hidden.method.HideForContextFacetViaMethod;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertyClearFacetViaClearMethod;
+import org.apache.isis.core.progmodel.facets.properties.modify.PropertySetterFacetViaModifyMethod;
+import org.apache.isis.core.progmodel.facets.properties.validate.PropertyValidateFacetViaMethod;
+import org.apache.isis.progmodel.wrapper.applib.DisabledException;
+import org.apache.isis.progmodel.wrapper.applib.HiddenException;
+import org.apache.isis.progmodel.wrapper.applib.InteractionException;
+import org.apache.isis.progmodel.wrapper.applib.InvalidException;
+import org.apache.isis.progmodel.wrapper.applib.WrapperFactory;
+import org.apache.isis.progmodel.wrapper.applib.WrapperFactory.ExecutionMode;
+import org.apache.isis.progmodel.wrapper.applib.WrapperObject;
+
+public class DomainObjectInvocationHandler<T> extends DelegatingInvocationHandlerDefault<T> {
+
+ private final Map<Method, Collection<?>> collectionViewObjectsByMethod = new HashMap<Method, Collection<?>>();
+ private final Map<Method, Map<?, ?>> mapViewObjectsByMethod = new HashMap<Method, Map<?, ?>>();
+
+ private final AuthenticationSessionProvider authenticationSessionProvider;
+ private final SpecificationLoader specificationLookup;
+ private final AdapterManager adapterManager;
+ private final ObjectPersistor objectPersistor;
+
+ /**
+ * The <tt>title()</tt> method; may be <tt>null</tt>.
+ */
+ protected Method titleMethod;
+
+ /**
+ * The <tt>save()</tt> method from {@link WrapperObject#save()}.
+ */
+ protected Method saveMethod;
+
+ /**
+ * The <tt>underlying()</tt> method from {@link WrapperObject#wrapped()}.
+ */
+ protected Method wrappedMethod;
+
+ public DomainObjectInvocationHandler(final T delegate, final WrapperFactory embeddedViewer, final ExecutionMode mode, final AuthenticationSessionProvider authenticationSessionProvider, final SpecificationLoader specificationLookup, final AdapterManager adapterManager,
+ final ObjectPersistor objectPersistor) {
+ super(delegate, embeddedViewer, mode);
+
+ this.authenticationSessionProvider = authenticationSessionProvider;
+ this.specificationLookup = specificationLookup;
+ this.adapterManager = adapterManager;
+ this.objectPersistor = objectPersistor;
+
+ try {
+ titleMethod = delegate.getClass().getMethod("title", new Class[] {});
+ saveMethod = WrapperObject.class.getMethod("save", new Class[] {});
+ wrappedMethod = WrapperObject.class.getMethod("wrapped", new Class[] {});
+ } catch (final NoSuchMethodException e) {
+ }
+ }
+
+ @Override
+ public Object invoke(final Object proxyObject, final Method method, final Object[] args) throws Throwable {
+
+ if (isObjectMethod(method)) {
+ return delegate(method, args);
+ }
+
+ final ObjectAdapter targetAdapter = getAdapterManager().getAdapterFor(getDelegate());
+
+ if (isTitleMethod(method)) {
+ return handleTitleMethod(method, args, targetAdapter);
+ }
+
+ final ObjectSpecification targetNoSpec = targetAdapter.getSpecification();
+
+ // save method, through the proxy
+ if (isSaveMethod(method)) {
+ return handleSaveMethod(getAuthenticationSession(), targetAdapter, targetNoSpec);
+ }
+
+ if (isUnderlyingMethod(method)) {
+ return getDelegate();
+ }
+
+ final ObjectMember objectMember = locateAndCheckMember(method);
+ final List<Facet> imperativeFacets = getImperativeFacets(objectMember, method);
+
+ final String memberName = objectMember.getName();
+
+ if (instanceOf(imperativeFacets, DisableForContextFacetViaMethod.class, HideForContextFacetViaMethod.class)) {
+ throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'", memberName));
+ }
+
+ final String methodName = method.getName();
+
+ if (instanceOf(imperativeFacets, ActionDefaultsFacet.class, PropertyDefaultFacet.class, ActionChoicesFacet.class, ActionParameterChoicesFacet.class, PropertyChoicesFacet.class)) {
+ return method.invoke(getDelegate(), args);
+ }
+
+ // for all members, check visibility and usability
+ checkVisibility(getAuthenticationSession(), targetAdapter, objectMember);
+
+ if (objectMember.isOneToOneAssociation()) {
+
+ if (instanceOf(imperativeFacets, PropertyValidateFacetViaMethod.class, PropertySetterFacetViaModifyMethod.class, PropertyClearFacetViaClearMethod.class)) {
+ throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'; use only property accessor/mutator", memberName));
+ }
+
+ final OneToOneAssociation otoa = (OneToOneAssociation) objectMember;
+ if (instanceOf(imperativeFacets, PropertyOrCollectionAccessorFacet.class)) {
+ return handleGetterMethodOnProperty(args, targetAdapter, otoa, methodName);
+ }
+ if (instanceOf(imperativeFacets, PropertySetterFacet.class, PropertyInitializationFacet.class)) {
+ checkUsability(getAuthenticationSession(), targetAdapter, objectMember);
+ return handleSetterMethodOnProperty(args, getAuthenticationSession(), targetAdapter, otoa, methodName);
+ }
+ }
+ if (objectMember.isOneToManyAssociation()) {
+
+ if (instanceOf(imperativeFacets, CollectionValidateAddToFacetViaMethod.class, CollectionValidateRemoveFromFacetViaMethod.class)) {
+ throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'; use only collection accessor/mutator", memberName));
+ }
+
+ final OneToManyAssociation otma = (OneToManyAssociation) objectMember;
+ if (instanceOf(imperativeFacets, PropertyOrCollectionAccessorFacet.class)) {
+ return handleGetterMethodOnCollection(method, args, targetAdapter, otma, memberName);
+ }
+ if (instanceOf(imperativeFacets, CollectionAddToFacet.class)) {
+ checkUsability(getAuthenticationSession(), targetAdapter, objectMember);
+ return handleCollectionAddToMethod(args, targetAdapter, otma, methodName);
+ }
+ if (instanceOf(imperativeFacets, CollectionRemoveFromFacet.class)) {
+ checkUsability(getAuthenticationSession(), targetAdapter, objectMember);
+ return handleCollectionRemoveFromMethod(args, targetAdapter, otma, methodName);
+ }
+ }
+
+ // filter out
+ if (instanceOf(imperativeFacets, PropertyOrCollectionAccessorFacet.class)) {
+ throw new UnsupportedOperationException(String.format("Can only invoke accessor on properties or collections; '%s' represents %s", methodName, decode(objectMember)));
+ }
+ if (instanceOf(imperativeFacets, PropertySetterFacet.class)) {
+ throw new UnsupportedOperationException(String.format("Can only invoke mutator on properties; '%s' represents %s", methodName, decode(objectMember)));
+ }
+ if (instanceOf(imperativeFacets, CollectionAddToFacet.class)) {
+ throw new UnsupportedOperationException(String.format("Can only invoke 'adder' on collections; '%s' represents %s", methodName, decode(objectMember)));
+ }
+ if (instanceOf(imperativeFacets, CollectionRemoveFromFacet.class)) {
+ throw new UnsupportedOperationException(String.format("Can only invoke 'remover' on collections; '%s' represents %s", methodName, decode(objectMember)));
+ }
+
+ if (objectMember instanceof ObjectAction) {
+
+ if (instanceOf(imperativeFacets, ActionValidationFacetViaMethod.class)) {
+ throw new UnsupportedOperationException(String.format("Cannot invoke supporting method '%s'; use only the 'invoke' method", memberName));
+ }
+
+ checkUsability(getAuthenticationSession(), targetAdapter, objectMember);
+
+ final ObjectAction noa = (ObjectAction) objectMember;
+ return handleActionMethod(args, getAuthenticationSession(), targetAdapter, noa, memberName);
+ }
+
+ throw new UnsupportedOperationException(String.format("Unknown member type '%s'", objectMember));
+ }
+
+ public List<Facet> getImperativeFacets(final ObjectMember objectMember, final Method method) {
+ final List<Facet> imperativeFacets = objectMember.getFacets(new Filter<Facet>() {
+ @Override
+ public boolean accept(final Facet facet) {
+ final ImperativeFacet imperativeFacet = asImperativeFacet(facet);
+ if (imperativeFacet == null) {
+ return false;
+ }
+ return imperativeFacet.getMethods().contains(method);
+ }
+
+ private ImperativeFacet asImperativeFacet(final Facet facet) {
+ if (facet == null) {
+ return null;
+ }
+ if (facet instanceof ImperativeFacet) {
+ return (ImperativeFacet) facet;
+ }
+ return asImperativeFacet(facet.getUnderlyingFacet());
+ }
+ });
+
+ // there will be at least one
+ if (imperativeFacets.isEmpty()) {
+ throw new IllegalStateException("should be at least one imperative facet");
+ }
+ return imperativeFacets;
+ }
+
+ private static boolean instanceOf(final List<?> objects, final Class<?>... superTypes) {
+ for (final Class<?> superType : superTypes) {
+ for (final Object obj : objects) {
+ if (superType.isAssignableFrom(obj.getClass())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // title
+ // /////////////////////////////////////////////////////////////////
+
+ private Object handleTitleMethod(final Method method, final Object[] args, final ObjectAdapter targetAdapter) throws IllegalAccessException, InvocationTargetException {
+
+ resolveIfRequired(targetAdapter);
+
+ final ObjectSpecification targetNoSpec = targetAdapter.getSpecification();
+ final ObjectTitleContext titleContext = targetNoSpec.createTitleInteractionContext(getAuthenticationSession(), InteractionInvocationMethod.BY_USER, targetAdapter);
+ final ObjectTitleEvent titleEvent = titleContext.createInteractionEvent();
+ notifyListeners(titleEvent);
+ return titleEvent.getTitle();
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // save
+ // /////////////////////////////////////////////////////////////////
+
+ private Object handleSaveMethod(final AuthenticationSession session, final ObjectAdapter targetAdapter, final ObjectSpecification targetNoSpec) {
+
+ final InteractionResult interactionResult = targetNoSpec.isValidResult(targetAdapter);
+ notifyListenersAndVetoIfRequired(interactionResult);
+
+ if (getExecutionMode() == ExecutionMode.EXECUTE) {
+ if (targetAdapter.isTransient()) {
+ getObjectPersistor().makePersistent(targetAdapter);
+ }
+ }
+ return null;
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // property - access
+ // /////////////////////////////////////////////////////////////////
+
+ private Object handleGetterMethodOnProperty(final Object[] args, final ObjectAdapter targetAdapter, final OneToOneAssociation otoa, final String methodName) {
+ if (args.length != 0) {
+ throw new IllegalArgumentException("Invoking a 'get' should have no arguments");
+ }
+
+ resolveIfRequired(targetAdapter);
+
+ final ObjectAdapter currentReferencedAdapter = otoa.get(targetAdapter);
+ final Object currentReferencedObj = AdapterUtils.unwrap(currentReferencedAdapter);
+
+ final PropertyAccessEvent ev = new PropertyAccessEvent(getDelegate(), otoa.getIdentifier(), currentReferencedObj);
+ notifyListeners(ev);
+ return currentReferencedObj;
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // property - modify
+ // /////////////////////////////////////////////////////////////////
+
+ private Object handleSetterMethodOnProperty(final Object[] args, final AuthenticationSession session, final ObjectAdapter targetAdapter, final OneToOneAssociation otoa, final String methodName) {
+ if (args.length != 1) {
+ throw new IllegalArgumentException("Invoking a setter should only have a single argument");
+ }
+
+ resolveIfRequired(targetAdapter);
+
+ final Object argumentObj = underlying(args[0]);
+ final ObjectAdapter argumentAdapter = argumentObj != null ? getAdapterManager().adapterFor(argumentObj) : null;
+
+ final InteractionResult interactionResult = otoa.isAssociationValid(targetAdapter, argumentAdapter).getInteractionResult();
+ notifyListenersAndVetoIfRequired(interactionResult);
+
+ if (getExecutionMode() == ExecutionMode.EXECUTE) {
+ otoa.set(targetAdapter, argumentAdapter);
+ }
+
+ objectChangedIfRequired(targetAdapter);
+
+ return null;
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // collection - access
+ // /////////////////////////////////////////////////////////////////
+
+ private Object handleGetterMethodOnCollection(final Method method, final Object[] args, final ObjectAdapter targetAdapter, final OneToManyAssociation otma, final String memberName) {
+ if (args.length != 0) {
+ throw new IllegalArgumentException("Invoking a 'get' should have no arguments");
+ }
+
+ resolveIfRequired(targetAdapter);
+
+ final ObjectAdapter currentReferencedAdapter = otma.get(targetAdapter);
+ final Object currentReferencedObj = AdapterUtils.unwrap(currentReferencedAdapter);
+
+ final CollectionAccessEvent ev = new CollectionAccessEvent(getDelegate(), otma.getIdentifier());
+
+ if (currentReferencedObj instanceof Collection) {
+ final Collection<?> collectionViewObject = lookupViewObject(method, memberName, (Collection<?>) currentReferencedObj, otma);
+ notifyListeners(ev);
+ return collectionViewObject;
+ } else if (currentReferencedObj instanceof Map) {
+ final Map<?, ?> mapViewObject = lookupViewObject(method, memberName, (Map<?, ?>) currentReferencedObj, otma);
+ notifyListeners(ev);
+ return mapViewObject;
+ }
+ throw new IllegalArgumentException(String.format("Collection type '%s' not supported by framework", currentReferencedObj.getClass().getName()));
+ }
+
+ /**
+ * Looks up (or creates) a proxy for this object.
+ */
+ private Collection<?> lookupViewObject(final Method method, final String memberName, final Collection<?> collectionToLookup, final OneToManyAssociation otma) {
+ Collection<?> collectionViewObject = collectionViewObjectsByMethod.get(method);
+ if (collectionViewObject == null) {
+ if (collectionToLookup instanceof WrapperObject) {
+ collectionViewObject = collectionToLookup;
+ } else {
+ collectionViewObject = Proxy.proxy(collectionToLookup, memberName, this, otma);
+ }
+ collectionViewObjectsByMethod.put(method, collectionViewObject);
+ }
+ return collectionViewObject;
+ }
+
+ private Map<?, ?> lookupViewObject(final Method method, final String memberName, final Map<?, ?> mapToLookup, final OneToManyAssociation otma) {
+ Map<?, ?> mapViewObject = mapViewObjectsByMethod.get(method);
+ if (mapViewObject == null) {
+ if (mapToLookup instanceof WrapperObject) {
+ mapViewObject = mapToLookup;
+ } else {
+ mapViewObject = Proxy.proxy(mapToLookup, memberName, this, otma);
+ }
+ mapViewObjectsByMethod.put(method, mapViewObject);
+ }
+ return mapViewObject;
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // collection - add to
+ // /////////////////////////////////////////////////////////////////
+
+ private Object handleCollectionAddToMethod(final Object[] args, final ObjectAdapter targetAdapter, final OneToManyAssociation otma, final String methodName) {
+
+ if (args.length != 1) {
+ throw new IllegalArgumentException("Invoking a addTo should only have a single argument");
+ }
+
+ resolveIfRequired(targetAdapter);
+
+ final Object argumentObj = underlying(args[0]);
+ if (argumentObj == null) {
+ throw new IllegalArgumentException("Must provide a non-null object to add");
+ }
+ final ObjectAdapter argumentNO = getAdapterManager().adapterFor(argumentObj);
+
+ final InteractionResult interactionResult = otma.isValidToAdd(targetAdapter, argumentNO).getInteractionResult();
+ notifyListenersAndVetoIfRequired(interactionResult);
+
+ if (getExecutionMode() == ExecutionMode.EXECUTE) {
+ otma.addElement(targetAdapter, argumentNO);
+ }
+
+ objectChangedIfRequired(targetAdapter);
+
+ return null;
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // collection - remove from
+ // /////////////////////////////////////////////////////////////////
+
+ private Object handleCollectionRemoveFromMethod(final Object[] args, final ObjectAdapter targetAdapter, final OneToManyAssociation otma, final String methodName) {
+ if (args.length != 1) {
+ throw new IllegalArgumentException("Invoking a removeFrom should only have a single argument");
+ }
+
+ resolveIfRequired(targetAdapter);
+
+ final Object argumentObj = underlying(args[0]);
+ if (argumentObj == null) {
+ throw new IllegalArgumentException("Must provide a non-null object to remove");
+ }
+ final ObjectAdapter argumentAdapter = getAdapterManager().adapterFor(argumentObj);
+
+ final InteractionResult interactionResult = otma.isValidToRemove(targetAdapter, argumentAdapter).getInteractionResult();
+ notifyListenersAndVetoIfRequired(interactionResult);
+
+ if (getExecutionMode() == ExecutionMode.EXECUTE) {
+ otma.removeElement(targetAdapter, argumentAdapter);
+ }
+
+ objectChangedIfRequired(targetAdapter);
+
+ return null;
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // action
+ // /////////////////////////////////////////////////////////////////
+
+ private Object handleActionMethod(final Object[] args, final AuthenticationSession session, final ObjectAdapter targetAdapter, final ObjectAction noa, final String memberName) {
+
+ final Object[] underlyingArgs = new Object[args.length];
+ int i = 0;
+ for (final Object arg : args) {
+ underlyingArgs[i++] = underlying(arg);
+ }
+
+ final ObjectAdapter[] argAdapters = new ObjectAdapter[underlyingArgs.length];
+ int j = 0;
+ for (final Object underlyingArg : underlyingArgs) {
+ argAdapters[j++] = underlyingArg != null ? getAdapterManager().adapterFor(underlyingArg) : null;
+ }
+
+ final InteractionResult interactionResult = noa.isProposedArgumentSetValid(targetAdapter, argAdapters).getInteractionResult();
+ notifyListenersAndVetoIfRequired(interactionResult);
+
+ if (getExecutionMode() == ExecutionMode.EXECUTE) {
+ final ObjectAdapter actionReturnNO = noa.execute(targetAdapter, argAdapters);
+ return AdapterUtils.unwrap(actionReturnNO);
+ }
+
+ objectChangedIfRequired(targetAdapter);
+
+ return null;
+ }
+
+ private Object underlying(final Object arg) {
+ if (arg instanceof WrapperObject) {
+ final WrapperObject argViewObject = (WrapperObject) arg;
+ return argViewObject.wrapped();
+ } else {
+ return arg;
+ }
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // visibility and usability checks (common to all members)
+ // /////////////////////////////////////////////////////////////////
+
+ /**
+ * REVIEW: ideally should provide some way to allow to caller to indicate the 'where' context. Having
+ * a hard-coded value like this is an approximation.
+ */
+ private final Where where = Where.ANYWHERE;
+
+ private void checkVisibility(final AuthenticationSession session, final ObjectAdapter targetObjectAdapter, final ObjectMember objectMember) {
+ final Consent visibleConsent = objectMember.isVisible(getAuthenticationSession(), targetObjectAdapter, where);
+ final InteractionResult interactionResult = visibleConsent.getInteractionResult();
+ notifyListenersAndVetoIfRequired(interactionResult);
+ }
+
+ private void checkUsability(final AuthenticationSession session, final ObjectAdapter targetObjectAdapter, final ObjectMember objectMember) {
+ final InteractionResult interactionResult = objectMember.isUsable(getAuthenticationSession(), targetObjectAdapter, where).getInteractionResult();
+ notifyListenersAndVetoIfRequired(interactionResult);
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // notify listeners
+ // /////////////////////////////////////////////////////////////////
+
+ private void notifyListenersAndVetoIfRequired(final InteractionResult interactionResult) {
+ final InteractionEvent interactionEvent = interactionResult.getInteractionEvent();
+ notifyListeners(interactionEvent);
+ if (interactionEvent.isVeto()) {
+ throw toException(interactionEvent);
+ }
+ }
+
+ private String decode(final ObjectMember objectMember) {
+ if (objectMember instanceof OneToOneAssociation) {
+ return "a property";
+ }
+ if (objectMember instanceof OneToManyAssociation) {
+ return "a collection";
+ }
+ if (objectMember instanceof ObjectAction) {
+ return "an action";
+ }
+ return "an UNKNOWN member type";
+ }
+
+ /**
+ * Wraps a {@link InteractionEvent#isVeto() vetoing}
+ * {@link InteractionEvent} in a corresponding {@link InteractionException},
+ * and returns it.
+ */
+ private InteractionException toException(final InteractionEvent interactionEvent) {
+ if (!interactionEvent.isVeto()) {
+ throw new IllegalArgumentException("Provided interactionEvent must be a veto");
+ }
+ if (interactionEvent instanceof ValidityEvent) {
+ final ValidityEvent validityEvent = (ValidityEvent) interactionEvent;
+ return new InvalidException(validityEvent);
+ }
+ if (interactionEvent instanceof VisibilityEvent) {
+ final VisibilityEvent visibilityEvent = (VisibilityEvent) interactionEvent;
+ return new HiddenException(visibilityEvent);
+ }
+ if (interactionEvent instanceof UsabilityEvent) {
+ final UsabilityEvent usabilityEvent = (UsabilityEvent) interactionEvent;
+ return new DisabledException(usabilityEvent);
+ }
+ throw new IllegalArgumentException("Provided interactionEvent must be a VisibilityEvent, UsabilityEvent or a ValidityEvent");
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // switching
+ // /////////////////////////////////////////////////////////////////
+
+ private ObjectMember locateAndCheckMember(final Method method) {
+ final ObjectSpecificationDefault objectSpecificationDefault = getJavaSpecificationOfOwningClass(method);
+ final ObjectMember member = objectSpecificationDefault.getMember(method);
+ if (member == null) {
+ final String methodName = method.getName();
+ throw new UnsupportedOperationException("Method '" + methodName + "' being invoked does not correspond to any of the object's fields or actions.");
+ }
+ return member;
+ }
+
+ protected boolean isTitleMethod(final Method method) {
+ return method.equals(titleMethod);
+ }
+
+ protected boolean isSaveMethod(final Method method) {
+ return method.equals(saveMethod);
+ }
+
+ protected boolean isUnderlyingMethod(final Method method) {
+ return method.equals(wrappedMethod);
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // Specification lookup
+ // /////////////////////////////////////////////////////////////////
+
+ private ObjectSpecificationDefault getJavaSpecificationOfOwningClass(final Method method) {
+ return getJavaSpecification(method.getDeclaringClass());
+ }
+
+ private ObjectSpecificationDefault getJavaSpecification(final Class<?> clazz) {
+ final ObjectSpecification nos = getSpecification(clazz);
+ if (!(nos instanceof ObjectSpecificationDefault)) {
+ throw new UnsupportedOperationException("Only Java is supported (specification is '" + nos.getClass().getCanonicalName() + "')");
+ }
+ return (ObjectSpecificationDefault) nos;
+ }
+
+ private ObjectSpecification getSpecification(final Class<?> type) {
+ final ObjectSpecification nos = getSpecificationLookup().loadSpecification(type);
+ return nos;
+ }
+
+ // /////////////////////////////////////////////////////////////////
+ // Dependencies
+ // /////////////////////////////////////////////////////////////////
+
+ protected SpecificationLoader getSpecificationLookup() {
+ return specificationLookup;
+ }
+
+ public AuthenticationSessionProvider getAuthenticationSessionProvider() {
+ return authenticationSessionProvider;
+ }
+
+ protected AuthenticationSession getAuthenticationSession() {
+ return getAuthenticationSessionProvider().getAuthenticationSession();
+ }
+
+ protected AdapterManager getAdapterManager() {
+ return adapterManager;
+ }
+
+ protected ObjectPersistor getObjectPersistor() {
+ return objectPersistor;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/IClassInstantiatorCE.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/IClassInstantiatorCE.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/IClassInstantiatorCE.java
new file mode 100644
index 0000000..d551c72
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/IClassInstantiatorCE.java
@@ -0,0 +1,37 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+/**
+ * Used to instantiate a given class.
+ */
+interface IClassInstantiatorCE {
+
+ /**
+ * Return a new instance of the specified class. The recommended way is
+ * without calling any constructor. This is usually done by doing like
+ * <code>ObjectInputStream.readObject()</code> which is JVM specific.
+ *
+ * @param c
+ * Class to instantiate
+ * @return new instance of clazz
+ */
+ Object newInstance(Class<?> clazz) throws InstantiationException;
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/IProxyFactory.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/IProxyFactory.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/IProxyFactory.java
new file mode 100644
index 0000000..c92c0e8
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/IProxyFactory.java
@@ -0,0 +1,28 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import java.lang.reflect.InvocationHandler;
+
+public interface IProxyFactory<T> {
+ T createProxy(Class<T> toProxyClass, InvocationHandler handler);
+
+ T createProxy(T toProxy, InvocationHandler handler);
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/InteractionEventDispatcher.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/InteractionEventDispatcher.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/InteractionEventDispatcher.java
new file mode 100644
index 0000000..0515501
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/InteractionEventDispatcher.java
@@ -0,0 +1,28 @@
+/*
+ * 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.progmodel.wrapper.metamodel.internal;
+
+import org.apache.isis.applib.events.InteractionEvent;
+
+public interface InteractionEventDispatcher {
+
+ void dispatch(InteractionEvent interactionEvent);
+
+}
http://git-wip-us.apache.org/repos/asf/isis/blob/cd9f2e4a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/InteractionEventDispatcherTypeSafe.java
----------------------------------------------------------------------
diff --git a/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/InteractionEventDispatcherTypeSafe.java b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/InteractionEventDispatcherTypeSafe.java
new file mode 100644
index 0000000..5ae80f6
--- /dev/null
+++ b/framework/progmodel/wrapper/wrapper-metamodel/src/main/java/org/apache/isis/progmodel/wrapper/metamodel/internal/InteractionEventDispatcherTypeSafe.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.progmodel.wrapper.metamodel.internal;
+
+import org.apache.isis.applib.events.InteractionEvent;
+
+public abstract class InteractionEventDispatcherTypeSafe<T extends InteractionEvent> implements InteractionEventDispatcher {
+
+ public abstract void dispatchTypeSafe(T interactionEvent);
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void dispatch(final InteractionEvent interactionEvent) {
+ dispatchTypeSafe((T) interactionEvent);
+ }
+
+}