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 2015/12/11 17:01:44 UTC

[1/2] isis git commit: ISIS-1237: factored out Generation context class to keep track of references vs definitions; factored out Tagger; factored out ClassExcluder.

Repository: isis
Updated Branches:
  refs/heads/master c64caa1ae -> 2efbf48f5


http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGeneratorTest.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGeneratorTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGeneratorTest.java
deleted file mode 100644
index cbebd55..0000000
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGeneratorTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *  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.services.swagger.internal;
-
-import org.junit.Test;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-
-public class SwaggerSpecGeneratorTest {
-
-    public static class TagFor extends SwaggerSpecGeneratorTest {
-
-        @Test
-        public void fullyQualifiedClass() throws Exception {
-            String tag = SwaggerSpecGenerator.tagFor("foo.bar.Abc");
-            assertThat(tag, is(equalTo("bar")));
-        }
-
-        @Test
-        public void jaxb() throws Exception {
-            String tag = SwaggerSpecGenerator.tagFor("todoapp.app.viewmodels.todoitem.v1_0.ToDoItemDto");
-            assertThat(tag, is(equalTo("todoitem")));
-        }
-
-        @Test
-        public void schemaClass() throws Exception {
-            String tag = SwaggerSpecGenerator.tagFor("bar.Abc");
-            assertThat(tag, is(equalTo("bar")));
-        }
-
-        @Test
-        public void noPackage() throws Exception {
-            String tag = SwaggerSpecGenerator.tagFor("Abc");
-            assertThat(tag, is(equalTo("Abc")));
-        }
-
-        @Test
-        public void isisAddons() throws Exception {
-            String tag = SwaggerSpecGenerator.tagFor("org.isisaddons.module.security.app.feature.ApplicationClass");
-            assertThat(tag, is(equalTo("isisaddons security")));
-        }
-
-        @Test
-        public void applib() throws Exception {
-            String tag = SwaggerSpecGenerator.tagFor("org.apache.isis.applib.fixturescripts.FixtureResult");
-            assertThat(tag, is(equalTo("> apache isis internals")));
-        }
-
-    }
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/TaggerTest.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/TaggerTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/TaggerTest.java
new file mode 100644
index 0000000..945d73e
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/TaggerTest.java
@@ -0,0 +1,69 @@
+/*
+ *  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.services.swagger.internal;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class TaggerTest {
+
+    public static class TagFor extends TaggerTest {
+
+        @Test
+        public void fullyQualifiedClass() throws Exception {
+            String tag = new Tagger().tagFor("foo.bar.Abc", null);
+            assertThat(tag, is(equalTo("bar")));
+        }
+
+        @Test
+        public void jaxb() throws Exception {
+            String tag = new Tagger().tagFor("todoapp.app.viewmodels.todoitem.v1_0.ToDoItemDto", null);
+            assertThat(tag, is(equalTo("todoitem")));
+        }
+
+        @Test
+        public void schemaClass() throws Exception {
+            String tag = new Tagger().tagFor("bar.Abc", null);
+            assertThat(tag, is(equalTo("bar")));
+        }
+
+        @Test
+        public void noPackage() throws Exception {
+            String tag = new Tagger().tagFor("Abc", null);
+            assertThat(tag, is(equalTo("Abc")));
+        }
+
+        @Test
+        public void isisAddons() throws Exception {
+            String tag = new Tagger().tagFor("org.isisaddons.module.security.app.feature.ApplicationClass", null);
+            assertThat(tag, is(equalTo("isisaddons security")));
+        }
+
+        @Test
+        public void applib() throws Exception {
+            String tag = new Tagger().tagFor("org.apache.isis.applib.fixturescripts.FixtureResult", null);
+            assertThat(tag, is(equalTo("> apache isis internals")));
+        }
+
+    }
+
+}
\ No newline at end of file


[2/2] isis git commit: ISIS-1237: factored out Generation context class to keep track of references vs definitions; factored out Tagger; factored out ClassExcluder.

Posted by da...@apache.org.
ISIS-1237: factored out Generation context class to keep track of references vs definitions; factored out Tagger; factored out ClassExcluder.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/2efbf48f
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/2efbf48f
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/2efbf48f

Branch: refs/heads/master
Commit: 2efbf48f5ea8a4d65e6a80361c8977d4e7a28ac1
Parents: c64caa1
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Fri Dec 11 15:53:26 2015 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Fri Dec 11 15:53:26 2015 +0000

----------------------------------------------------------------------
 .../swagger/internal/ClassExcluder.java         |  52 ++
 .../services/swagger/internal/Generation.java   | 831 +++++++++++++++++++
 .../swagger/internal/SwaggerSpecGenerator.java  | 770 +----------------
 .../services/swagger/internal/Tagger.java       |  61 ++
 .../services/swagger/internal/Util.java         |   6 +-
 .../swagger/internal/ValuePropertyFactory.java  |   2 +
 .../swagger/internal/GenerationTest.java        |  48 ++
 .../internal/SwaggerSpecGeneratorTest.java      |  69 --
 .../services/swagger/internal/TaggerTest.java   |  69 ++
 9 files changed, 1071 insertions(+), 837 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ClassExcluder.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ClassExcluder.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ClassExcluder.java
new file mode 100644
index 0000000..6ae8deb
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ClassExcluder.java
@@ -0,0 +1,52 @@
+/*
+ *  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.services.swagger.internal;
+
+import java.util.List;
+
+import org.apache.isis.applib.fixturescripts.FixtureResult;
+import org.apache.isis.applib.fixturescripts.FixtureScript;
+import org.apache.isis.core.metamodel.spec.ObjectSpecification;
+import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
+
+public class ClassExcluder {
+
+    protected boolean exclude(final ObjectSpecification objectSpec) {
+        if(objectSpec == null) {
+            return false;
+        }
+        Class<?> correspondingClass = objectSpec.getCorrespondingClass();
+        return correspondingClass == FixtureResult.class || FixtureScript.class.isAssignableFrom(correspondingClass) || correspondingClass == FixtureScript.ExecutionContext.class;
+    }
+
+    public boolean exclude(final ObjectAction objectAction) {
+        final ObjectSpecification returnType = objectAction.getReturnType();
+        if(exclude(returnType)) {
+            return true;
+        }
+
+        final List<ObjectSpecification> parameterTypes = objectAction.getParameterTypes();
+        for (ObjectSpecification parameterType : parameterTypes) {
+            if(exclude(parameterType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
new file mode 100644
index 0000000..1cce186
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Generation.java
@@ -0,0 +1,831 @@
+/*
+ *  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.services.swagger.internal;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.apache.isis.applib.annotation.ActionSemantics;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.services.swagger.SwaggerService;
+import org.apache.isis.core.commons.factory.InstanceUtil;
+import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
+import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
+import org.apache.isis.core.metamodel.facets.object.mixin.MixinFacet;
+import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
+import org.apache.isis.core.metamodel.services.ServiceUtil;
+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.ObjectActionParameter;
+import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
+
+import io.swagger.models.Info;
+import io.swagger.models.ModelImpl;
+import io.swagger.models.Operation;
+import io.swagger.models.Path;
+import io.swagger.models.Response;
+import io.swagger.models.Swagger;
+import io.swagger.models.parameters.BodyParameter;
+import io.swagger.models.parameters.PathParameter;
+import io.swagger.models.parameters.QueryParameter;
+import io.swagger.models.properties.ArrayProperty;
+import io.swagger.models.properties.MapProperty;
+import io.swagger.models.properties.ObjectProperty;
+import io.swagger.models.properties.Property;
+import io.swagger.models.properties.RefProperty;
+import io.swagger.models.properties.StringProperty;
+
+class Generation {
+
+    private final String basePath;
+    private final SwaggerService.Visibility visibility;
+    private final SpecificationLoader specificationLoader;
+
+    private final ValuePropertyFactory valuePropertyFactory;
+    private final Tagger tagger;
+    private final ClassExcluder classExcluder;
+
+    private final Set<String> references = Sets.newLinkedHashSet();
+    private final Set<String> definitions = Sets.newLinkedHashSet();
+    private Swagger swagger;
+
+    public Generation(
+            final String basePath,
+            final SwaggerService.Visibility visibility,
+            final SpecificationLoader specificationLoader) {
+        this.basePath = basePath;
+        this.visibility = visibility;
+        this.specificationLoader = specificationLoader;
+
+        this.valuePropertyFactory = newValuePropertyFactory();
+        this.tagger = newTagger();
+        this.classExcluder = newClassExcluder();
+    }
+
+    protected ValuePropertyFactory newValuePropertyFactory() {
+        return new ValuePropertyFactory();
+    }
+
+    protected ClassExcluder newClassExcluder() {
+        return new ClassExcluder();
+    }
+
+    protected Tagger newTagger() {
+        return new Tagger();
+    }
+
+    Swagger generate() {
+        this.swagger = new Swagger();
+
+        swagger.basePath(basePath);
+        swagger.info(new Info()
+                .version("0.0.0") // TODO: provide some way of passing the name, version etc (some sort of SPI service?)
+                .title(visibility.name() + " API")
+        );
+
+
+        appendRestfulObjectsSupportingPathsAndDefinitions();
+        appendLinkModelDefinition();
+
+        appendServicePathsAndDefinitions();
+        appendObjectPathsAndDefinitions();
+
+        appendDefinitionsForOrphanedReferences();
+
+        return swagger;
+    }
+
+    void appendServicePathsAndDefinitions() {
+        // take copy to avoid concurrent modification exception
+        final Collection<ObjectSpecification> allSpecs = Lists.newArrayList(specificationLoader.allSpecifications());
+        for (final ObjectSpecification serviceSpec :  allSpecs) {
+
+            final DomainServiceFacet domainServiceFacet = serviceSpec.getFacet(DomainServiceFacet.class);
+            if (domainServiceFacet == null) {
+                continue;
+            }
+            if (visibility.isPublic() &&
+                    domainServiceFacet.getNatureOfService() != NatureOfService.VIEW_REST_ONLY) {
+                continue;
+            }
+            if (domainServiceFacet.getNatureOfService() != NatureOfService.VIEW_MENU_ONLY &&
+                    domainServiceFacet.getNatureOfService() != NatureOfService.VIEW) {
+                continue;
+            }
+
+            final List<ObjectAction> serviceActions = Util.actionsOf(serviceSpec, visibility, classExcluder);
+            if(serviceActions.isEmpty()) {
+                continue;
+            }
+            appendServicePath(serviceSpec);
+
+            for (final ObjectAction serviceAction : serviceActions) {
+                appendServiceActionInvokePath(serviceSpec, serviceAction);
+            }
+        }
+    }
+
+    void appendObjectPathsAndDefinitions() {
+        // take copy to avoid concurrent modification exception
+        final Collection<ObjectSpecification> allSpecs = Lists.newArrayList(specificationLoader.allSpecifications());
+        for (final ObjectSpecification objectSpec : allSpecs) {
+
+            final DomainServiceFacet domainServiceFacet = objectSpec.getFacet(DomainServiceFacet.class);
+            if (domainServiceFacet != null) {
+                continue;
+            }
+            final MixinFacet mixinFacet = objectSpec.getFacet(MixinFacet.class);
+            if (mixinFacet != null) {
+                continue;
+            }
+            if(visibility.isPublic() && !Util.isVisibleForPublic(objectSpec)) {
+                continue;
+            }
+            if(objectSpec.isAbstract()) {
+                continue;
+            }
+            if(objectSpec.isValue()) {
+                continue;
+            }
+            // special cases
+            if(classExcluder.exclude(objectSpec)) {
+                continue;
+            }
+
+            final List<OneToOneAssociation> objectProperties = Util.propertiesOf(objectSpec, visibility);
+            final List<OneToManyAssociation> objectCollections = Util.collectionsOf(objectSpec, visibility);
+            final List<ObjectAction> objectActions = Util.actionsOf(objectSpec, visibility, classExcluder);
+
+            if(objectProperties.isEmpty() && objectCollections.isEmpty()) {
+                continue;
+            }
+            final ModelImpl isisModel = appendObjectPathAndModelDefinitions(objectSpec);
+            updateObjectModel(isisModel, objectSpec, objectProperties, objectCollections);
+
+            for (final OneToManyAssociation objectCollection : objectCollections) {
+                appendCollectionTo(objectSpec, objectCollection);
+            }
+
+            for (final ObjectAction objectAction : objectActions) {
+                appendObjectActionInvokePath(objectSpec, objectAction);
+            }
+        }
+    }
+
+    void appendRestfulObjectsSupportingPathsAndDefinitions() {
+
+        final String tag = "> restful objects supporting resources";
+
+        swagger.path("/",
+                new Path()
+                        .get(new Operation()
+                                .tag(tag)
+                                .description(Util.roSpec("5.1"))
+                                .produces("application/json")
+                                .produces("application/json;profile=urn:org.restfulobjects:repr-types/home-page")
+                                .response(200,
+                                        newResponse(Caching.NON_EXPIRING)
+                                                .description("OK")
+                                                .schema(newRefProperty("RestfulObjectsSupportingHomePageRepr"))
+                                )));
+        addDefinition("RestfulObjectsSupportingHomePageRepr", newModel(Util.roSpec("5.2")));
+
+        swagger.path("/user",
+                new Path()
+                        .get(new Operation()
+                                .tag(tag)
+                                .description(Util.roSpec("6.1"))
+                                .produces("application/json")
+                                .produces("application/json;profile=urn:org.restfulobjects:repr-types/user")
+                                .response(200,
+                                        newResponse(Caching.USER_INFO)
+                                                .description("OK")
+                                                .schema(newRefProperty("RestfulObjectsSupportingUserRepr"))
+                                )));
+        addDefinition("RestfulObjectsSupportingUserRepr",
+                newModel(Util.roSpec("6.2"))
+                        .property("userName", stringProperty())
+                        .property("roles", arrayOfStrings())
+                        .property("links", arrayOfLinks())
+                        .required("userName")
+                        .required("roles"));
+
+        swagger.path("/services",
+                new Path()
+                        .get(new Operation()
+                                .tag(tag)
+                                .description(Util.roSpec("7.1"))
+                                .produces("application/json")
+                                .produces("application/json;profile=urn:org.restfulobjects:repr-types/services")
+                                .response(200,
+                                        newResponse(Caching.USER_INFO)
+                                                .description("OK")
+                                                .schema(newRefProperty("RestfulObjectsSupportingServicesRepr"))
+                                )));
+        addDefinition("RestfulObjectsSupportingServicesRepr",
+                newModel(Util.roSpec("7.2"))
+                        .property("value", arrayOfLinks())
+                        .required("userName")
+                        .required("roles"));
+
+        swagger.path("/version",
+                new Path()
+                        .get(new Operation()
+                                .tag(tag)
+                                .description(Util.roSpec("8.1"))
+                                .produces("application/json")
+                                .produces("application/json;profile=urn:org.restfulobjects:repr-types/RestfulObjectsSupportingServicesRepr")
+                                .response(200,
+                                        newResponse(Caching.NON_EXPIRING)
+                                                .description("OK")
+                                                .schema(new ObjectProperty())
+                                )));
+        swagger.addDefinition("RestfulObjectsSupportingServicesRepr",
+                newModel(Util.roSpec("8.2"))
+                        .property("specVersion", stringProperty())
+                        .property("implVersion", stringProperty())
+                        .property("optionalCapabilities",
+                                new ObjectProperty()
+                                        .property("blobsClobs", stringProperty())
+                                        .property("deleteObjects", stringProperty())
+                                        .property("domainModel", stringProperty())
+                                        .property("validateOnly", stringProperty())
+                                        .property("protoPersistentObjects", stringProperty())
+                        )
+                        .required("userName")
+                        .required("roles"));
+    }
+
+    void appendLinkModelDefinition() {
+        swagger.addDefinition("LinkRepr",
+                new ModelImpl()
+                        .type("object")
+                        .property("rel", stringProperty().description("the relationship of the resource to this referencing resource"))
+                        .property("href", stringProperty().description("the hyperlink reference (URL) of the resource"))
+                        .property("title", stringProperty().description("title to render"))
+                        .property("method", stringPropertyEnum("GET", "POST", "PUT", "DELETE").description("HTTP verb to access"))
+                        .property("type", stringProperty().description("Content-Type recognized by the resource (for HTTP Accept header)"))
+                        .property("arguments", new ObjectProperty().description("Any arguments, to send as query strings or in body"))
+                        .property("value", stringProperty().description("the representation of the link if followed"))
+                        .required("rel")
+                        .required("href")
+                        .required("method")
+        );
+
+        swagger.addDefinition("HrefRepr",
+                new ModelImpl()
+                        .type("object")
+                        .description("Abbreviated version of the Link resource, used primarily to reference non-value objects")
+                        .property("href", stringProperty().description("the hyperlink reference (URL) of the resource"))
+                        .required("href")
+        );
+
+    }
+
+    void appendServicePath(final ObjectSpecification objectSpec) {
+
+        final String serviceId = serviceIdFor(objectSpec);
+
+        final Path path = new Path();
+        swagger.path(String.format("/services/%s", serviceId), path);
+
+        final String serviceModelDefinition = serviceId + "Repr";
+
+        final String tag = tagFor(serviceId, "> services");
+        path.get(
+                new Operation()
+                        .tag(tag)
+                        .description(Util.roSpec("15.1"))
+                        .produces("application/json")
+                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/object")
+                        .response(200,
+                                newResponse(Caching.TRANSACTIONAL)
+                                        .description("OK")
+                                        .schema(newRefProperty(serviceModelDefinition)))
+        );
+
+        final ModelImpl model =
+                newModel(Util.roSpec("15.1.2") + ": representation of " + serviceId)
+                        .property("title", stringProperty())
+                        .property("serviceId", stringProperty()._default(serviceId))
+                        .property("members", new ObjectProperty());
+
+        addDefinition(serviceModelDefinition, model);
+    }
+
+    ModelImpl appendObjectPathAndModelDefinitions(final ObjectSpecification objectSpec) {
+
+        final String objectType = objectTypeFor(objectSpec);
+
+        final Path path = new Path();
+        swagger.path(String.format("/objects/%s/{objectId}", objectType), path);
+
+        final String tag = tagFor(objectType, null);
+        final Operation operation = new Operation();
+        path.get(operation);
+        operation
+                .tag(tag)
+                .description(Util.roSpec("14.1"))
+                .parameter(
+                        new PathParameter()
+                                .name("objectId")
+                                .type("string"))
+                .produces("application/json;profile=urn:org.apache.isis/v1")
+                .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+                .produces("application/json;profile=urn:org.restfulobjects:repr-types/object");
+
+
+        // per https://github.com/swagger-api/swagger-spec/issues/146, swagger 2.0 doesn't support multiple
+        // modelled representations per path and response code;
+        // in particular cannot associate representation/model with Accept header ('produces(...) method)
+        final String restfulObjectsModelDefinition = objectType + "RestfulObjectsRepr";
+        if (false) {
+            operation.response(200,
+                    newResponse(Caching.TRANSACTIONAL)
+                            .description("if Accept: application/json;profile=urn:org.restfulobjects:repr-types/object")
+                            .schema(newRefProperty(restfulObjectsModelDefinition)));
+
+            final ModelImpl roSpecModel =
+                    newModel(Util.roSpec("14.4") + ": representation of " + objectType)
+                            .property("title", stringProperty())
+                            .property("domainType", stringProperty()._default(objectType))
+                            .property("instanceId", stringProperty())
+                            .property("members", new ObjectProperty());
+            swagger.addDefinition(restfulObjectsModelDefinition, roSpecModel);
+        }
+
+        final String isisModelDefinition = objectType + "Repr";
+        operation
+                .response(200,
+                        newResponse(Caching.TRANSACTIONAL)
+                                .description(objectType + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
+                                .schema(newRefProperty(isisModelDefinition)));
+
+        final ModelImpl isisModel = new ModelImpl();
+        addDefinition(isisModelDefinition, isisModel);
+
+        // return so can be appended to
+        return isisModel;
+    }
+
+    // UNUSED
+    void appendServiceActionPromptTo(final ObjectProperty serviceMembers, final ObjectAction action) {
+        String actionId = action.getId();
+
+        serviceMembers.property(actionId,
+                new ObjectProperty()
+                        .property("id", stringPropertyEnum(actionId))
+                        .property("memberType", stringPropertyEnum("action"))
+                        .property("links",
+                                new ObjectProperty()
+                                        .property("rel", stringPropertyEnum( String.format(
+                                                "urn:org.restfulobjects:rels/details;action=%s", actionId)))
+                                        .property("href", stringPropertyEnum(String.format(
+                                                "actions/%s", actionId))))
+                        .property("method", stringPropertyEnum("GET"))
+                        .property("type", stringPropertyEnum(
+                                "application/json;profile=urn:org.restfulobjects:repr-types/object-action"))
+        );
+    }
+
+    void appendServiceActionInvokePath(
+            final ObjectSpecification serviceSpec,
+            final ObjectAction serviceAction) {
+
+        final String serviceId = serviceIdFor(serviceSpec);
+        final String actionId = serviceAction.getId();
+
+        final List<ObjectActionParameter> parameters = serviceAction.getParameters();
+        final Path path = new Path();
+        swagger.path(String.format("/services/%s/actions/%s/invoke", serviceId, actionId), path);
+
+        final String tag = tagFor(serviceId, "> services");
+        final Operation invokeOperation =
+                new Operation()
+                        .tag(tag)
+                        .description(Util.roSpec("19.1") + ": (invoke) resource of " + serviceId + "#" + actionId)
+                        .produces("application/json;profile=urn:org.apache.isis/v1")
+                        .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/action-result")
+                ;
+
+        final ActionSemantics.Of semantics = serviceAction.getSemantics();
+        if(semantics.isSafeInNature()) {
+            path.get(invokeOperation);
+
+            for (final ObjectActionParameter parameter : parameters) {
+                invokeOperation
+                        .parameter(
+                                new QueryParameter()
+                                        .name(parameter.getId())
+                                        .description(Util.roSpec("2.9.1") + (!Strings.isNullOrEmpty(parameter.getDescription())? (": " + parameter.getDescription()) : ""))
+                                        .required(false)
+                                        .type("string")
+                        );
+            }
+            if(!parameters.isEmpty()) {
+                invokeOperation.parameter(new QueryParameter()
+                        .name("x-isis-querystring")
+                        .description(Util.roSpec("2.10") + ": all (formal) arguments as base64 encoded string")
+                        .required(false)
+                        .type("string"));
+            }
+
+        } else {
+            if (semantics.isIdempotentInNature()) {
+                path.put(invokeOperation);
+            } else {
+                path.post(invokeOperation);
+            }
+
+            final ModelImpl bodyParam =
+                    new ModelImpl()
+                            .type("object");
+            for (final ObjectActionParameter parameter : parameters) {
+
+                final Property valueProperty;
+                // TODO: need to switch on parameter's type and create appropriate impl of valueProperty
+                // if(parameter.getSpecification().isValue()) ...
+                valueProperty = stringProperty();
+
+                bodyParam
+                        .property(parameter.getId(),
+                                new ObjectProperty()
+                                        .property("value", valueProperty)
+                        );
+            }
+
+            invokeOperation
+                    .consumes("application/json")
+                    .parameter(
+                            new BodyParameter()
+                                    .name("body")
+                                    .schema(bodyParam));
+
+        }
+
+        invokeOperation
+                .response(
+                        200, new Response()
+                                .description(serviceId + "#" + actionId + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
+                                .schema(actionReturnTypeFor(serviceAction))
+                );
+    }
+
+    void appendCollectionTo(
+            final ObjectSpecification objectSpec,
+            final OneToManyAssociation collection) {
+
+        final String objectType = objectTypeFor(objectSpec);
+        final String collectionId = collection.getId();
+
+        final Path path = new Path();
+        swagger.path(String.format("/objects/%s/{objectId}/collections/%s", objectType, collectionId), path);
+
+        final String tag = tagFor(objectType, null);
+        final Operation collectionOperation =
+                new Operation()
+                        .tag(tag)
+                        .description(Util.roSpec("17.1") + ": resource of " + objectType + "#" + collectionId)
+                        .parameter(
+                                new PathParameter()
+                                        .name("objectId")
+                                        .type("string"))
+                        .produces("application/json;profile=urn:org.apache.isis/v1")
+                        .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/object-collection");
+
+        path.get(collectionOperation);
+        collectionOperation
+                .response(
+                        200, new Response()
+                                .description(objectType + "#" + collectionId + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
+                                .schema(modelFor(collection))
+                );
+    }
+
+    void appendObjectActionInvokePath(
+            final ObjectSpecification objectSpec,
+            final ObjectAction objectAction) {
+
+        final String objectType = objectTypeFor(objectSpec);
+        final String actionId = objectAction.getId();
+
+        final List<ObjectActionParameter> parameters = objectAction.getParameters();
+        final Path path = new Path();
+        swagger.path(String.format("/objects/%s/{objectId}/actions/%s/invoke", objectType, actionId), path);
+
+        final String tag = tagFor(objectType, null);
+        final Operation invokeOperation =
+                new Operation()
+                        .tag(tag)
+                        .description(Util.roSpec("19.1") + ": (invoke) resource of " + objectType + "#" + actionId)
+                        .parameter(
+                                new PathParameter()
+                                        .name("objectId")
+                                        .type("string"))
+                        .produces("application/json;profile=urn:org.apache.isis/v1")
+                        .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
+                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/action-result");
+
+        final ActionSemantics.Of semantics = objectAction.getSemantics();
+        if(semantics.isSafeInNature()) {
+            path.get(invokeOperation);
+
+            for (final ObjectActionParameter parameter : parameters) {
+                invokeOperation
+                        .parameter(
+                                new QueryParameter()
+                                        .name(parameter.getId())
+                                        .description(Util.roSpec("2.9.1") + (!Strings.isNullOrEmpty(parameter.getDescription())? (": " + parameter.getDescription()) : ""))
+                                        .required(false)
+                                        .type("string")
+                        );
+            }
+            if(!parameters.isEmpty()) {
+                invokeOperation.parameter(new QueryParameter()
+                        .name("x-isis-querystring")
+                        .description(Util.roSpec("2.10") + ": all (formal) arguments as base64 encoded string")
+                        .required(false)
+                        .type("string"));
+            }
+
+        } else {
+            if (semantics.isIdempotentInNature()) {
+                path.put(invokeOperation);
+            } else {
+                path.post(invokeOperation);
+            }
+
+            final ModelImpl bodyParam =
+                    new ModelImpl()
+                            .type("object");
+            for (final ObjectActionParameter parameter : parameters) {
+
+                final ObjectSpecification specification = parameter.getSpecification();
+                final Property valueProperty = specification.isValue() ? modelFor(specification) : refToLinkModel() ;
+                bodyParam
+                        .property(parameter.getId(),
+                                new ObjectProperty()
+                                        .property("value", valueProperty)
+                        );
+            }
+
+            invokeOperation
+                    .consumes("application/json")
+                    .parameter(
+                            new BodyParameter()
+                                    .name("body")
+                                    .schema(bodyParam));
+
+        }
+
+        invokeOperation
+                .response(
+                        200, new Response()
+                                .description(objectType + "#" + actionId)
+                                .schema(actionReturnTypeFor(objectAction))
+                );
+    }
+
+    void appendDefinitionsForOrphanedReferences() {
+        final Set<String> referencesWithoutDefinition = getReferencesWithoutDefinition();
+        for (String reference : referencesWithoutDefinition) {
+            swagger.addDefinition(reference, new ModelImpl());
+        }
+    }
+
+    Property actionReturnTypeFor(final ObjectAction objectAction) {
+
+        final ObjectSpecification specification = objectAction.getReturnType();
+        TypeOfFacet typeOfFacet = objectAction.getFacet(TypeOfFacet.class);
+        if(typeOfFacet != null) {
+            ObjectSpecification elementSpec = typeOfFacet.valueSpec();
+            if(elementSpec != null) {
+                return arrayPropertyOf(elementSpec);
+            }
+        }
+        return modelFor(specification);
+    }
+
+    private Property modelFor(final OneToManyAssociation collection) {
+        ObjectSpecification collectionSpecification = collection.getSpecification();
+        return arrayPropertyOf(collectionSpecification);
+    }
+
+    private Property arrayPropertyOf(final ObjectSpecification objectSpecification) {
+        final ArrayProperty arrayProperty = new ArrayProperty();
+        if(objectSpecification != null && objectSpecification.getCorrespondingClass() != Object.class) {
+            arrayProperty
+                    .description("List of " + objectTypeFor(objectSpecification))
+                    .items(modelFor(objectSpecification));
+        } else {
+            arrayProperty.items(new ObjectProperty());
+        }
+        return arrayProperty;
+    }
+
+    private Property modelFor(final ObjectSpecification specification) {
+        if(specification == null) {
+            return new ObjectProperty();
+        }
+
+        // no "simple" representation for void or values
+        final Class<?> correspondingClass = specification.getCorrespondingClass();
+        if(correspondingClass == void.class || correspondingClass == Void.class) {
+            return new ObjectProperty();
+        }
+        // no "simple" representation for values
+        final Property property = valuePropertyFactory.newProperty(correspondingClass);
+        if(property != null) {
+            // was recognized as a value
+            return new ObjectProperty();
+        }
+
+        if(specification.isParentedOrFreeCollection()) {
+            TypeOfFacet typeOfFacet = specification.getFacet(TypeOfFacet.class);
+            if(typeOfFacet != null) {
+                ObjectSpecification elementSpec = typeOfFacet.valueSpec();
+                if(elementSpec != null) {
+                    return arrayPropertyOf(elementSpec);
+                }
+            }
+        }
+
+        if(specification.getCorrespondingClass() == java.lang.Object.class) {
+            return new ObjectProperty();
+        }
+        if(specification.getCorrespondingClass() == java.lang.Enum.class) {
+            return new StringProperty();
+        }
+        return newRefProperty(objectTypeFor(specification) + "Repr");
+    }
+
+    void updateObjectModel(
+            final ModelImpl model,
+            final ObjectSpecification objectSpecification,
+            final List<OneToOneAssociation> objectProperties,
+            final List<OneToManyAssociation> objectCollections) {
+
+        final String objectType = objectTypeFor(objectSpecification);
+        final String className = objectSpecification.getFullIdentifier();
+
+        model
+                .type("object")
+                .description(String.format("%s (%s)", objectType, className));
+
+        for (OneToOneAssociation objectProperty : objectProperties) {
+            model.property(
+                    objectProperty.getId(),
+                    propertyFor(objectProperty.getSpecification()));
+        }
+
+        for (OneToManyAssociation objectCollection : objectCollections) {
+            final ObjectSpecification elementSpec = objectCollection.getSpecification();
+            model.property(
+                    objectCollection.getId(),
+                    arrayPropertyOf(elementSpec)
+            );
+        }
+    }
+
+    Property propertyFor(final ObjectSpecification objectSpecification) {
+        final Property property =
+                valuePropertyFactory.newProperty(objectSpecification.getCorrespondingClass());
+        if (property != null) {
+            return property;
+        }
+        else {
+            // assume this is a reference to an entity/view model, meaning we use an href
+            return refToHrefModel();
+        }
+    }
+
+    // unused
+    static String roSpecForResponseOf(final ObjectAction action) {
+        final ActionSemantics.Of semantics = action.getSemantics();
+        switch (semantics) {
+        case SAFE_AND_REQUEST_CACHEABLE:
+        case SAFE:
+            return "19.2";
+        case IDEMPOTENT:
+        case IDEMPOTENT_ARE_YOU_SURE:
+            return "19.3";
+        default:
+            return "19.4";
+        }
+    }
+
+    static ModelImpl newModel(final String description) {
+        return new ModelImpl()
+                .description(description)
+                .type("object")
+                .property("links", arrayOfLinks())
+                .property("extensions", new MapProperty())
+                .required("links")
+                .required("extensions");
+    }
+
+    // TODO: this is horrid, there ought to be a facet we can call instead...
+    static String serviceIdFor(final ObjectSpecification serviceSpec) {
+        Object tempServiceInstance = InstanceUtil.createInstance(serviceSpec.getCorrespondingClass());
+        return serviceId(tempServiceInstance);
+    }
+
+    static String serviceId(final Object object) {
+        return ServiceUtil.id(object);
+    }
+
+    static String objectTypeFor(final ObjectSpecification objectSpec) {
+        return objectSpec.getFacet(ObjectSpecIdFacet.class).value().asString();
+    }
+
+    static StringProperty stringProperty() {
+        return new StringProperty();
+    }
+
+    static StringProperty stringPropertyEnum(String... enumValues) {
+        StringProperty stringProperty = stringProperty();
+        stringProperty._enum(Arrays.asList(enumValues));
+        if(enumValues.length >= 1) {
+            stringProperty._default(enumValues[0]);
+        }
+        return stringProperty;
+    }
+
+    static ArrayProperty arrayOfLinks() {
+        return new ArrayProperty()
+                .items(refToLinkModel());
+    }
+
+    static RefProperty refToLinkModel() {
+        return new RefProperty("#/definitions/LinkRepr");
+    }
+
+    static RefProperty refToHrefModel() {
+        return new RefProperty("#/definitions/HrefRepr");
+    }
+
+    static ArrayProperty arrayOfStrings() {
+        return new ArrayProperty().items(stringProperty());
+    }
+
+    static Response newResponse(final Caching caching) {
+        return Util.withCachingHeaders(new Response(), caching);
+    }
+
+    String tagFor(final String str, final String fallback) {
+        return tagger.tagFor(str, fallback);
+    }
+
+    private Property newRefProperty(final String model) {
+        addSwaggerReference(model);
+        return new RefProperty("#/definitions/" + model);
+    }
+
+    private void addDefinition(final String key, final ModelImpl model) {
+        addSwaggerDefinition(key);
+        swagger.addDefinition(key, model);
+    }
+
+    void addSwaggerReference(final String model) {
+        references.add(model);
+    }
+
+    void addSwaggerDefinition(final String model) {
+        definitions.add(model);
+    }
+
+    Set<String> getReferencesWithoutDefinition() {
+        LinkedHashSet<String> referencesCopy = Sets.newLinkedHashSet(references);
+        referencesCopy.removeAll(definitions);
+        return referencesCopy;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java
index b1eaf1d..25cb917 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/SwaggerSpecGenerator.java
@@ -18,57 +18,18 @@
  */
 package org.apache.isis.core.metamodel.services.swagger.internal;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import com.fasterxml.jackson.core.JsonProcessingException;
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
 
-import org.apache.isis.applib.annotation.ActionSemantics;
-import org.apache.isis.applib.annotation.NatureOfService;
-import org.apache.isis.applib.fixturescripts.FixtureResult;
 import org.apache.isis.applib.services.swagger.SwaggerService;
-import org.apache.isis.core.commons.exceptions.IsisException;
-import org.apache.isis.core.commons.factory.InstanceUtil;
-import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet;
-import org.apache.isis.core.metamodel.facets.object.domainservice.DomainServiceFacet;
-import org.apache.isis.core.metamodel.facets.object.mixin.MixinFacet;
-import org.apache.isis.core.metamodel.facets.object.objectspecid.ObjectSpecIdFacet;
-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.ObjectActionParameter;
-import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
-import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 
-import io.swagger.models.Info;
-import io.swagger.models.ModelImpl;
-import io.swagger.models.Operation;
-import io.swagger.models.Path;
-import io.swagger.models.Response;
 import io.swagger.models.Swagger;
-import io.swagger.models.parameters.BodyParameter;
-import io.swagger.models.parameters.PathParameter;
-import io.swagger.models.parameters.QueryParameter;
-import io.swagger.models.properties.ArrayProperty;
-import io.swagger.models.properties.MapProperty;
-import io.swagger.models.properties.ObjectProperty;
-import io.swagger.models.properties.Property;
-import io.swagger.models.properties.RefProperty;
-import io.swagger.models.properties.StringProperty;
 import io.swagger.util.Json;
 import io.swagger.util.Yaml;
 
 public class SwaggerSpecGenerator {
 
     private final SpecificationLoader specificationLoader;
-    private final ValuePropertyFactory valuePropertyFactory = new ValuePropertyFactory();
 
     public SwaggerSpecGenerator(final SpecificationLoader specificationLoader) {
         this.specificationLoader = specificationLoader;
@@ -79,18 +40,8 @@ public class SwaggerSpecGenerator {
             final SwaggerService.Visibility visibility,
             final SwaggerService.Format format) {
 
-        final Swagger swagger = new Swagger();
-        swagger.basePath(basePath);
-        swagger.info(new Info()
-                        //.version("1.0.0") // TODO: provide some way of passing the name, version etc (some sort of SPI service?)
-                        .title(visibility.name() + " API")
-        );
-
-        appendRestfulObjectsSupportingPathsAndDefinitions(swagger);
-        appendLinkModelDefinition(swagger);
-
-        appendServicePathsAndDefinitions(swagger, visibility);
-        appendObjectPathsAndDefinitions(swagger, visibility);
+        final Generation generation = newGeneration(basePath, visibility);
+        final Swagger swagger = generation.generate();
 
         switch (format) {
             case JSON:
@@ -106,721 +57,8 @@ public class SwaggerSpecGenerator {
         }
     }
 
-    void appendServicePathsAndDefinitions(final Swagger swagger, final SwaggerService.Visibility visibility) {
-        // take copy to avoid concurrent modification exception
-        final Collection<ObjectSpecification> allSpecs = Lists.newArrayList(specificationLoader.allSpecifications());
-        for (final ObjectSpecification serviceSpec :  allSpecs) {
-
-            final DomainServiceFacet domainServiceFacet = serviceSpec.getFacet(DomainServiceFacet.class);
-            if (domainServiceFacet == null) {
-                continue;
-            }
-            if (visibility.isPublic() &&
-                domainServiceFacet.getNatureOfService() != NatureOfService.VIEW_REST_ONLY) {
-                continue;
-            }
-            if (domainServiceFacet.getNatureOfService() != NatureOfService.VIEW_MENU_ONLY &&
-                domainServiceFacet.getNatureOfService() != NatureOfService.VIEW) {
-                continue;
-            }
-
-            final List<ObjectAction> serviceActions = Util.actionsOf(serviceSpec, visibility);
-            if(serviceActions.isEmpty()) {
-                continue;
-            }
-            appendServicePath(swagger, serviceSpec);
-
-            for (final ObjectAction serviceAction : serviceActions) {
-                appendServiceActionInvokePath(swagger, serviceSpec, serviceAction);
-            }
-        }
-    }
-
-    void appendObjectPathsAndDefinitions(final Swagger swagger, final SwaggerService.Visibility visibility) {
-        // take copy to avoid concurrent modification exception
-        final Collection<ObjectSpecification> allSpecs = Lists.newArrayList(specificationLoader.allSpecifications());
-        for (final ObjectSpecification objectSpec : allSpecs) {
-
-            final DomainServiceFacet domainServiceFacet = objectSpec.getFacet(DomainServiceFacet.class);
-            if (domainServiceFacet != null) {
-                continue;
-            }
-            final MixinFacet mixinFacet = objectSpec.getFacet(MixinFacet.class);
-            if (mixinFacet != null) {
-                continue;
-            }
-            if(visibility.isPublic() && !Util.isVisibleForPublic(objectSpec)) {
-                continue;
-            }
-            if(objectSpec.isAbstract()) {
-                continue;
-            }
-            if(objectSpec.isValue()) {
-                continue;
-            }
-            // special cases
-            if(objectSpec.getCorrespondingClass() == FixtureResult.class) {
-                continue;
-            }
-
-            final List<OneToOneAssociation> objectProperties = Util.propertiesOf(objectSpec, visibility);
-            final List<OneToManyAssociation> objectCollections = Util.collectionsOf(objectSpec, visibility);
-            final List<ObjectAction> objectActions = Util.actionsOf(objectSpec, visibility);
-
-            if(objectProperties.isEmpty() && objectCollections.isEmpty()) {
-                continue;
-            }
-            final ModelImpl isisModel = appendObjectPathAndModelDefinitions(swagger, objectSpec);
-            updateObjectModel(isisModel, objectSpec, objectProperties, objectCollections);
-
-            for (final OneToManyAssociation objectCollection : objectCollections) {
-                appendCollectionTo(swagger, objectSpec, objectCollection);
-            }
-
-            for (final ObjectAction objectAction : objectActions) {
-                appendObjectActionInvokePath(swagger, objectSpec, objectAction);
-            }
-        }
+    protected Generation newGeneration(final String basePath, final SwaggerService.Visibility visibility) {
+        return new Generation(basePath, visibility, specificationLoader);
     }
 
-    void appendRestfulObjectsSupportingPathsAndDefinitions(final Swagger swagger) {
-
-        final String tag = "> restful objects supporting resources";
-
-        swagger.path("/",
-                new Path()
-                        .get(new Operation()
-                                .tag(tag)
-                                .description(Util.roSpec("5.1"))
-                                .produces("application/json")
-                                .produces("application/json;profile=urn:org.restfulobjects:repr-types/home-page")
-                                .response(200,
-                                        newResponse(Caching.NON_EXPIRING)
-                                                .description("OK")
-                                                .schema(new RefProperty("#/definitions/RestfulObjectsSupportingHomePageModel"))
-                                )));
-        swagger.addDefinition("RestfulObjectsSupportingHomePageModel",
-                newModel(Util.roSpec("5.2")));
-
-        swagger.path("/user",
-                new Path()
-                        .get(new Operation()
-                                .tag(tag)
-                                .description(Util.roSpec("6.1"))
-                                .produces("application/json")
-                                .produces("application/json;profile=urn:org.restfulobjects:repr-types/user")
-                                .response(200,
-                                        newResponse(Caching.USER_INFO)
-                                                .description("OK")
-                                                .schema(new RefProperty("#/definitions/RestfulObjectsSupportingUserModel"))
-                                )));
-        swagger.addDefinition("RestfulObjectsSupportingUserModel",
-                newModel(Util.roSpec("6.2"))
-                        .property("userName", stringProperty())
-                        .property("roles", arrayOfStrings())
-                        .property("links", arrayOfLinks())
-                        .required("userName")
-                        .required("roles"));
-
-        swagger.path("/services",
-                new Path()
-                        .get(new Operation()
-                                .tag(tag)
-                                .description(Util.roSpec("7.1"))
-                                .produces("application/json")
-                                .produces("application/json;profile=urn:org.restfulobjects:repr-types/services")
-                                .response(200,
-                                        newResponse(Caching.USER_INFO)
-                                                .description("OK")
-                                                .schema(new RefProperty("#/definitions/RestfulObjectsSupportingServicesModel"))
-                                )));
-        swagger.addDefinition("RestfulObjectsSupportingServicesModel",
-                newModel(Util.roSpec("7.2"))
-                        .property("value", arrayOfLinks())
-                        .required("userName")
-                        .required("roles"));
-
-        swagger.path("/version",
-                new Path()
-                        .get(new Operation()
-                                .tag(tag)
-                                .description(Util.roSpec("8.1"))
-                                .produces("application/json")
-                                .produces("application/json;profile=urn:org.restfulobjects:repr-types/RestfulObjectsSupportingServicesModel")
-                                .response(200,
-                                        newResponse(Caching.NON_EXPIRING)
-                                                .description("OK")
-                                                .schema(new ObjectProperty())
-                                )));
-        swagger.addDefinition("RestfulObjectsSupportingServicesModel",
-                newModel(Util.roSpec("8.2"))
-                        .property("specVersion", stringProperty())
-                        .property("implVersion", stringProperty())
-                        .property("optionalCapabilities",
-                                new ObjectProperty()
-                                        .property("blobsClobs", stringProperty())
-                                        .property("deleteObjects", stringProperty())
-                                        .property("domainModel", stringProperty())
-                                        .property("validateOnly", stringProperty())
-                                        .property("protoPersistentObjects", stringProperty())
-                        )
-                        .required("userName")
-                        .required("roles"));
-    }
-
-    void appendLinkModelDefinition(final Swagger swagger) {
-        swagger.addDefinition("LinkModel",
-                new ModelImpl()
-                    .type("object")
-                    .property("rel", stringProperty().description("the relationship of the resource to this referencing resource"))
-                    .property("href", stringProperty().description("the hyperlink reference (URL) of the resource"))
-                    .property("title", stringProperty().description("title to render"))
-                    .property("method", stringPropertyEnum("GET", "POST", "PUT", "DELETE").description("HTTP verb to access"))
-                    .property("type", stringProperty().description("Content-Type recognized by the resource (for HTTP Accept header)"))
-                    .property("arguments", new ObjectProperty().description("Any arguments, to send as query strings or in body"))
-                    .property("value", stringProperty().description("the representation of the link if followed"))
-                    .required("rel")
-                    .required("href")
-                    .required("method")
-        );
-
-        swagger.addDefinition("HrefModel",
-                new ModelImpl()
-                    .type("object")
-                    .description("Abbreviated version of the Link resource, used primarily to reference non-value objects")
-                    .property("href", stringProperty().description("the hyperlink reference (URL) of the resource"))
-                    .required("href")
-        );
-
-    }
-
-    void appendServicePath(final Swagger swagger, final ObjectSpecification objectSpec) {
-
-        final String serviceId = serviceIdFor(objectSpec);
-
-        final Path path = new Path();
-        swagger.path(String.format("/services/%s", serviceId), path);
-
-        final String serviceModelDefinition = serviceId + "Model";
-
-        final String tag = tagFor(serviceId);
-        path.get(
-                new Operation()
-                        .tag(tag)
-                        .description(Util.roSpec("15.1"))
-                        .produces("application/json")
-                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/object")
-                        .response(200,
-                                newResponse(Caching.TRANSACTIONAL)
-                                        .description("OK")
-                                        .schema(new RefProperty("#/definitions/" + serviceModelDefinition)))
-        );
-
-        final ModelImpl model =
-                newModel(Util.roSpec("15.1.2") + ": representation of " + serviceId)
-                        .property("title", stringProperty())
-                        .property("serviceId", stringProperty()._default(serviceId))
-                        .property("members", new ObjectProperty());
-
-        swagger.addDefinition(serviceModelDefinition, model);
-    }
-
-    ModelImpl appendObjectPathAndModelDefinitions(final Swagger swagger, final ObjectSpecification objectSpec) {
-
-        final String objectType = objectTypeFor(objectSpec);
-
-        final Path path = new Path();
-        swagger.path(String.format("/objects/%s/{objectId}", objectType), path);
-
-        final String tag = tagFor(objectType);
-        final Operation operation = new Operation();
-        path.get(operation);
-        operation
-                .tag(tag)
-                .description(Util.roSpec("14.1"))
-                .parameter(
-                    new PathParameter()
-                        .name("objectId")
-                        .type("string"))
-                .produces("application/json;profile=urn:org.apache.isis/v1")
-                .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
-                .produces("application/json;profile=urn:org.restfulobjects:repr-types/object");
-
-
-        // per https://github.com/swagger-api/swagger-spec/issues/146, swagger 2.0 doesn't support multiple
-        // modelled representations per path and response code;
-        // in particular cannot associate representation/model with Accept header ('produces(...) method)
-        final String restfulObjectsModelDefinition = objectType + "RestfulObjectsModel";
-        if (false) {
-            operation.response(200,
-                    newResponse(Caching.TRANSACTIONAL)
-                            .description("if Accept: application/json;profile=urn:org.restfulobjects:repr-types/object")
-                            .schema(new RefProperty("#/definitions/" + restfulObjectsModelDefinition)));
-
-            final ModelImpl roSpecModel =
-                    newModel(Util.roSpec("14.4") + ": representation of " + objectType)
-                            .property("title", stringProperty())
-                            .property("domainType", stringProperty()._default(objectType))
-                            .property("instanceId", stringProperty())
-                            .property("members", new ObjectProperty());
-            swagger.addDefinition(restfulObjectsModelDefinition, roSpecModel);
-        }
-
-        final String isisModelDefinition = objectType + "Model";
-        operation
-                .response(200,
-                        newResponse(Caching.TRANSACTIONAL)
-                                .description(objectType + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
-                                .schema(new RefProperty("#/definitions/" + isisModelDefinition)));
-
-        final ModelImpl isisModel = new ModelImpl();
-        swagger.addDefinition(isisModelDefinition, isisModel);
-
-        // return so can be appended to
-        return isisModel;
-    }
-
-    // UNUSED
-    void appendServiceActionPromptTo(final ObjectProperty serviceMembers, final ObjectAction action) {
-        String actionId = action.getId();
-
-        serviceMembers.property(actionId,
-                new ObjectProperty()
-                        .property("id", stringPropertyEnum(actionId))
-                        .property("memberType", stringPropertyEnum("action"))
-                        .property("links",
-                                new ObjectProperty()
-                                        .property("rel", stringPropertyEnum( String.format(
-                                                "urn:org.restfulobjects:rels/details;action=%s", actionId)))
-                                        .property("href", stringPropertyEnum(String.format(
-                                                "actions/%s", actionId))))
-                        .property("method", stringPropertyEnum("GET"))
-                        .property("type", stringPropertyEnum(
-                                "application/json;profile=urn:org.restfulobjects:repr-types/object-action"))
-        );
-    }
-
-    void appendServiceActionInvokePath(
-            final Swagger swagger,
-            final ObjectSpecification serviceSpec,
-            final ObjectAction serviceAction) {
-
-        final String serviceId = serviceIdFor(serviceSpec);
-        final String actionId = serviceAction.getId();
-
-        final List<ObjectActionParameter> parameters = serviceAction.getParameters();
-        final Path path = new Path();
-        swagger.path(String.format("/services/%s/actions/%s/invoke", serviceId, actionId), path);
-
-        final String tag = tagFor(serviceId);
-        final Operation invokeOperation =
-                new Operation()
-                        .tag(tag)
-                        .description(Util.roSpec("19.1") + ": (invoke) resource of " + serviceId + "#" + actionId)
-                        .produces("application/json;profile=urn:org.apache.isis/v1")
-                        .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
-                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/action-result")
-                ;
-
-        final ActionSemantics.Of semantics = serviceAction.getSemantics();
-        if(semantics.isSafeInNature()) {
-            path.get(invokeOperation);
-
-            for (final ObjectActionParameter parameter : parameters) {
-                invokeOperation
-                        .parameter(
-                                new QueryParameter()
-                                        .name(parameter.getId())
-                                        .description(Util.roSpec("2.9.1") + (!Strings.isNullOrEmpty(parameter.getDescription())? (": " + parameter.getDescription()) : ""))
-                                        .required(false)
-                                        .type("string")
-                        );
-            }
-            if(!parameters.isEmpty()) {
-                invokeOperation.parameter(new QueryParameter()
-                        .name("x-isis-querystring")
-                        .description(Util.roSpec("2.10") + ": all (formal) arguments as base64 encoded string")
-                        .required(false)
-                        .type("string"));
-            }
-
-        } else {
-            if (semantics.isIdempotentInNature()) {
-                path.put(invokeOperation);
-            } else {
-                path.post(invokeOperation);
-            }
-
-            final ModelImpl bodyParam =
-                    new ModelImpl()
-                            .type("object");
-            for (final ObjectActionParameter parameter : parameters) {
-
-                final Property valueProperty;
-                // TODO: need to switch on parameter's type and create appropriate impl of valueProperty
-                // if(parameter.getSpecification().isValue()) ...
-                valueProperty = stringProperty();
-
-                bodyParam
-                        .property(parameter.getId(),
-                                new ObjectProperty()
-                                        .property("value", valueProperty)
-                        );
-            }
-
-            invokeOperation
-                    .consumes("application/json")
-                    .parameter(
-                            new BodyParameter()
-                                    .name("body")
-                                    .schema(bodyParam));
-
-        }
-
-        invokeOperation
-                .response(
-                        200, new Response()
-                                .description(serviceId + "#" + actionId + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
-                                .schema(actionReturnTypeFor(serviceAction))
-                );
-    }
-
-    void appendCollectionTo(
-            final Swagger swagger,
-            final ObjectSpecification objectSpec,
-            final OneToManyAssociation collection) {
-
-        final String objectType = objectTypeFor(objectSpec);
-        final String collectionId = collection.getId();
-
-        final Path path = new Path();
-        swagger.path(String.format("/objects/%s/{objectId}/collections/%s", objectType, collectionId), path);
-
-        final String tag = tagFor(objectType);
-        final Operation collectionOperation =
-                new Operation()
-                        .tag(tag)
-                        .description(Util.roSpec("17.1") + ": resource of " + objectType + "#" + collectionId)
-                        .parameter(
-                                new PathParameter()
-                                        .name("objectId")
-                                        .type("string"))
-                        .produces("application/json;profile=urn:org.apache.isis/v1")
-                        .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
-                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/object-collection");
-
-        path.get(collectionOperation);
-        collectionOperation
-                .response(
-                        200, new Response()
-                                .description(objectType + "#" + collectionId + " , if Accept: application/json;profile=urn:org.apache.isis/v1")
-                                .schema(modelFor(collection))
-                );
-    }
-
-    private Property modelFor(final OneToManyAssociation collection) {
-        ObjectSpecification collectionSpecification = collection.getSpecification();
-        return new ArrayProperty()
-                .description("List of " + objectTypeFor(collectionSpecification))
-                    .items(modelFor(collectionSpecification));
-    }
-
-    void appendObjectActionInvokePath(
-            final Swagger swagger,
-            final ObjectSpecification objectSpec,
-            final ObjectAction objectAction) {
-
-        final String objectType = objectTypeFor(objectSpec);
-        final String actionId = objectAction.getId();
-
-        final List<ObjectActionParameter> parameters = objectAction.getParameters();
-        final Path path = new Path();
-        swagger.path(String.format("/objects/%s/{objectId}/actions/%s/invoke", objectType, actionId), path);
-
-        final String tag = tagFor(objectType);
-        final Operation invokeOperation =
-                new Operation()
-                        .tag(tag)
-                        .description(Util.roSpec("19.1") + ": (invoke) resource of " + objectType + "#" + actionId)
-                        .parameter(
-                                new PathParameter()
-                                        .name("objectId")
-                                        .type("string"))
-                        .produces("application/json;profile=urn:org.apache.isis/v1")
-                        .produces("application/json;profile=urn:org.apache.isis/v1;suppress=true")
-                        .produces("application/json;profile=urn:org.restfulobjects:repr-types/action-result");
-
-        final ActionSemantics.Of semantics = objectAction.getSemantics();
-        if(semantics.isSafeInNature()) {
-            path.get(invokeOperation);
-
-            for (final ObjectActionParameter parameter : parameters) {
-                invokeOperation
-                        .parameter(
-                                new QueryParameter()
-                                        .name(parameter.getId())
-                                        .description(Util.roSpec("2.9.1") + (!Strings.isNullOrEmpty(parameter.getDescription())? (": " + parameter.getDescription()) : ""))
-                                        .required(false)
-                                        .type("string")
-                        );
-            }
-            if(!parameters.isEmpty()) {
-                invokeOperation.parameter(new QueryParameter()
-                        .name("x-isis-querystring")
-                        .description(Util.roSpec("2.10") + ": all (formal) arguments as base64 encoded string")
-                        .required(false)
-                        .type("string"));
-            }
-
-        } else {
-            if (semantics.isIdempotentInNature()) {
-                path.put(invokeOperation);
-            } else {
-                path.post(invokeOperation);
-            }
-
-            final ModelImpl bodyParam =
-                    new ModelImpl()
-                            .type("object");
-            for (final ObjectActionParameter parameter : parameters) {
-
-                final Property valueProperty;
-                // TODO: need to switch on parameter's type and create appropriate impl of valueProperty
-                // if(parameter.getSpecification().isValue()) ...
-                valueProperty = stringProperty();
-
-                bodyParam
-                        .property(parameter.getId(),
-                                new ObjectProperty()
-                                        .property("value", valueProperty)
-                        );
-            }
-
-            invokeOperation
-                    .consumes("application/json")
-                    .parameter(
-                            new BodyParameter()
-                                    .name("body")
-                                    .schema(bodyParam));
-
-        }
-
-        invokeOperation
-                .response(
-                        200, new Response()
-                                .description(objectType + "#" + actionId)
-                                .schema(actionReturnTypeFor(objectAction))
-                );
-    }
-
-    Property actionReturnTypeFor(final ObjectAction objectAction) {
-
-        final ObjectSpecification specification = objectAction.getReturnType();
-        TypeOfFacet typeOfFacet = objectAction.getFacet(TypeOfFacet.class);
-        if(typeOfFacet != null) {
-            ObjectSpecification elementSpec = typeOfFacet.valueSpec();
-            if(elementSpec != null) {
-                return new ArrayProperty()
-                        .items(modelFor(elementSpec));
-            }
-        }
-        return modelFor(specification);
-    }
-
-    private Property modelFor(final ObjectSpecification specification) {
-        if(specification == null) {
-            return new ObjectProperty();
-        }
-
-        // no "simple" representation for void or values
-        final Class<?> correspondingClass = specification.getCorrespondingClass();
-        if(correspondingClass == void.class || correspondingClass == Void.class) {
-            return new ObjectProperty();
-        }
-        // no "simple" representation for values
-        final Property property = valuePropertyFactory.newProperty(correspondingClass);
-        if(property != null) {
-            // was recognized as a value
-            return new ObjectProperty();
-        }
-
-        if(specification.isParentedOrFreeCollection()) {
-            TypeOfFacet typeOfFacet = specification.getFacet(TypeOfFacet.class);
-            if(typeOfFacet != null) {
-                ObjectSpecification elementSpec = typeOfFacet.valueSpec();
-                if(elementSpec != null) {
-                    return new ArrayProperty()
-                            .items(modelFor(elementSpec));
-                }
-            }
-        }
-
-        return new RefProperty("#/definitions/" + objectTypeFor(specification) + "Model");
-    }
-
-    void updateObjectModel(
-            final ModelImpl model,
-            final ObjectSpecification objectSpecification,
-            final List<OneToOneAssociation> objectProperties,
-            final List<OneToManyAssociation> objectCollections) {
-
-        final String objectType = objectTypeFor(objectSpecification);
-        final String className = objectSpecification.getFullIdentifier();
-
-        model
-                .type("object")
-                .description(String.format("%s (%s)", objectType, className));
-
-        for (OneToOneAssociation objectProperty : objectProperties) {
-            model.property(
-                    objectProperty.getId(),
-                    propertyFor(objectProperty.getSpecification()));
-        }
-
-        for (OneToManyAssociation objectCollection : objectCollections) {
-            final ObjectSpecification elementSpec = objectCollection.getSpecification();
-            final String elementModelDefinition = objectTypeFor(elementSpec) + "Model";
-
-            model.property(
-                    objectCollection.getId(),
-                    new ArrayProperty()
-                    .items(new RefProperty("#/definitions/" + elementModelDefinition))
-            );
-        }
-    }
-
-    Property propertyFor(final ObjectSpecification objectSpecification) {
-        final Property property =
-                valuePropertyFactory.newProperty(objectSpecification.getCorrespondingClass());
-        if (property != null) {
-            return property;
-        }
-        else {
-            // assume this is a reference to an entity/view model, meaning we use an href
-            return refToHrefModel();
-        }
-    }
-
-    static String roSpecForResponseOf(final ObjectAction action) {
-        final ActionSemantics.Of semantics = action.getSemantics();
-        switch (semantics) {
-            case SAFE_AND_REQUEST_CACHEABLE:
-            case SAFE:
-                return "19.2";
-            case IDEMPOTENT:
-            case IDEMPOTENT_ARE_YOU_SURE:
-                return "19.3";
-            default:
-                return "19.4";
-        }
-    }
-
-    static ModelImpl newModel(final String description) {
-        return new ModelImpl()
-                .description(description)
-                .type("object")
-                .property("links", arrayOfLinks())
-                .property("extensions", new MapProperty())
-                .required("links")
-                .required("extensions");
-    }
-
-    // TODO: this is horrid, there ought to be a facet we can call instead...
-    static String serviceIdFor(final ObjectSpecification serviceSpec) {
-        Object tempServiceInstance = InstanceUtil.createInstance(serviceSpec.getCorrespondingClass());
-        return serviceId(tempServiceInstance);
-    }
-
-    static String serviceId(final Object object) {
-        final Class<?> cls = object.getClass();
-        try {
-            final Method m = cls.getMethod("getId", new Class[0]);
-            return (String) m.invoke(object, new Object[0]);
-        } catch (final SecurityException e) {
-            throw new IsisException(e);
-        } catch (final NoSuchMethodException e) {
-            return object.getClass().getName();
-        } catch (final IllegalArgumentException e) {
-            throw new IsisException(e);
-        } catch (final IllegalAccessException e) {
-            throw new IsisException(e);
-        } catch (final InvocationTargetException e) {
-            throw new IsisException(e);
-        }
-    }
-
-    static String objectTypeFor(final ObjectSpecification objectSpec) {
-        return objectSpec.getFacet(ObjectSpecIdFacet.class).value().asString();
-    }
-
-    static StringProperty stringProperty() {
-        return new StringProperty();
-    }
-
-    static StringProperty stringPropertyEnum(String... enumValues) {
-        StringProperty stringProperty = stringProperty();
-        stringProperty._enum(Arrays.asList(enumValues));
-        if(enumValues.length >= 1) {
-            stringProperty._default(enumValues[0]);
-        }
-        return stringProperty;
-    }
-
-    static ArrayProperty arrayOfLinks() {
-        return new ArrayProperty()
-                .items(refToLinkModel());
-    }
-
-    static RefProperty refToLinkModel() {
-        return new RefProperty("#/definitions/LinkModel");
-    }
-
-    static RefProperty refToHrefModel() {
-        return new RefProperty("#/definitions/HrefModel");
-    }
-
-    static ArrayProperty arrayOfStrings() {
-        return new ArrayProperty().items(stringProperty());
-    }
-
-    static Response newResponse(final Caching caching) {
-        return Util.withCachingHeaders(new Response(), caching);
-    }
-
-    static Pattern tagPatternIsisAddons = Pattern.compile("^org\\.isisaddons\\.module\\.([^\\.]+)\\.(.+)$");
-    static Pattern tagPatternForFqcn = Pattern.compile("^.*\\.([^\\.]+)\\.([^\\.]+)$");
-    static Pattern tagPatternForSchemaTable = Pattern.compile("^([^\\.]+)\\.([^\\.]+)$");
-    static Pattern tagPatternForJaxbDto = Pattern.compile("^([^\\.]+)\\.(v[0-9][^\\.]*)([^\\.]+)$");
-    static String tagFor(final String str) {
-        if (str.startsWith("org.apache.isis")) {
-            return "> apache isis internals";
-        }
-
-        Matcher matcher;
-        matcher = tagPatternIsisAddons.matcher(str);
-        if (matcher.matches()) {
-            return "isisaddons " + matcher.group(1);
-        }
-        matcher = Pattern.compile("^.*\\.([^\\.]+)\\.(v[0-9][^\\.]*)\\.([^\\.]+)$").matcher(str);
-        if (matcher.matches()) {
-            return matcher.group(1);
-        }
-        matcher = tagPatternForFqcn.matcher(str);
-        if (matcher.matches()) {
-            return matcher.group(1);
-        }
-        matcher = tagPatternForSchemaTable.matcher(str);
-        if (matcher.matches()) {
-            // special cases for other Isis addons
-            if(str.startsWith("isis")) {
-                return "isisaddons " + str.substring(4, str.indexOf("."));
-            }
-            return matcher.group(1);
-        }
-
-
-        return str;
-    }
-
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Tagger.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Tagger.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Tagger.java
new file mode 100644
index 0000000..19f71d9
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Tagger.java
@@ -0,0 +1,61 @@
+/*
+ *  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.services.swagger.internal;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+class Tagger {
+
+    static Pattern tagPatternIsisAddons = Pattern.compile("^org\\.isisaddons\\.module\\.([^\\.]+)\\.(.+)$");
+    static Pattern tagPatternForFqcn = Pattern.compile("^.*\\.([^\\.]+)\\.([^\\.]+)$");
+    static Pattern tagPatternForSchemaTable = Pattern.compile("^([^\\.]+)\\.([^\\.]+)$");
+    static Pattern tagPatternForJaxbDto = Pattern.compile("^.*\\.([^\\.]+)\\.(v[0-9][^\\.]*)\\.([^\\.]+)$");
+
+    String tagFor(final String str, final String fallback) {
+        if (str.startsWith("org.apache.isis")) {
+            return "> apache isis internals";
+        }
+
+        Matcher matcher;
+        matcher = tagPatternIsisAddons.matcher(str);
+        if (matcher.matches()) {
+            return "isisaddons " + matcher.group(1);
+        }
+        matcher = tagPatternForJaxbDto.matcher(str);
+        if (matcher.matches()) {
+            return matcher.group(1);
+        }
+        matcher = tagPatternForFqcn.matcher(str);
+        if (matcher.matches()) {
+            return matcher.group(1);
+        }
+        matcher = tagPatternForSchemaTable.matcher(str);
+        if (matcher.matches()) {
+            // special cases for other Isis addons
+            if (str.startsWith("isis")) {
+                return "isisaddons " + str.substring(4, str.indexOf("."));
+            }
+            return matcher.group(1);
+        }
+
+        return fallback != null? fallback: str;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Util.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Util.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Util.java
index 4ab66e2..1ad9264 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Util.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/Util.java
@@ -129,13 +129,15 @@ public final class Util {
 
     static List<ObjectAction> actionsOf(
             final ObjectSpecification objectSpec,
-            final SwaggerService.Visibility visibility) {
+            final SwaggerService.Visibility visibility,
+            final ClassExcluder classExcluder) {
         final List<ActionType> actionTypes = actionTypesFor(visibility);
 
         return objectSpec.getObjectActions(actionTypes, Contributed.INCLUDED, new Filter<ObjectAction>() {
             @Override
             public boolean accept(final ObjectAction objectAction) {
-                return !visibility.isPublic() || isVisibleForPublic(objectAction);
+                return !classExcluder.exclude(objectAction) &&
+                        !visibility.isPublic() || isVisibleForPublic(objectAction);
             }
         });
     }

http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java
index c00990e..9b3a4d0 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/internal/ValuePropertyFactory.java
@@ -116,6 +116,8 @@ public class ValuePropertyFactory {
                 return new StringProperty();
             }
         };
+        propertyFactoryByClass.put(char.class, stringFactory);
+        propertyFactoryByClass.put(Character.class, stringFactory);
         propertyFactoryByClass.put(char[].class, stringFactory);
         propertyFactoryByClass.put(String.class, stringFactory);
 

http://git-wip-us.apache.org/repos/asf/isis/blob/2efbf48f/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/GenerationTest.java
----------------------------------------------------------------------
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/GenerationTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/GenerationTest.java
new file mode 100644
index 0000000..f89c9bf
--- /dev/null
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/services/swagger/internal/GenerationTest.java
@@ -0,0 +1,48 @@
+/*
+ *  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.services.swagger.internal;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.assertThat;
+
+public class GenerationTest {
+
+    @Test
+    public void testAddReference() throws Exception {
+
+        Generation context = new Generation(null, null, null);
+
+        context.addSwaggerReference("foo");
+        context.addSwaggerReference("bar");
+        context.addSwaggerReference("baz");
+
+        context.addSwaggerDefinition("foo");
+        context.addSwaggerDefinition("box");
+
+        Set<String> referencesWithoutDefinition = context.getReferencesWithoutDefinition();
+        assertThat(referencesWithoutDefinition.size(), is(2));
+        assertThat(referencesWithoutDefinition, contains("bar", "baz"));
+
+    }
+}
\ No newline at end of file