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 2011/10/31 20:11:31 UTC

svn commit: r1195624 - in /incubator/isis/trunk/framework: applib/src/docbkx/guide/ applib/src/main/java/org/apache/isis/applib/annotation/ core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/ core/progmodel/src/main/jav...

Author: danhaywood
Date: Mon Oct 31 19:11:30 2011
New Revision: 1195624

URL: http://svn.apache.org/viewvc?rev=1195624&view=rev
Log:
ISIS-111, ISIS-120: @Title annotation + docs

Added:
    incubator/isis/trunk/framework/applib/src/main/java/org/apache/isis/applib/annotation/Title.java
      - copied, changed from r1189396, incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/Title.java
Removed:
    incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/Title.java
Modified:
    incubator/isis/trunk/framework/applib/src/docbkx/guide/isis-applib.xml
    incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java
    incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
    incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java
    incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java
    incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleFacetViaTitleAnnotationTest.java

Modified: incubator/isis/trunk/framework/applib/src/docbkx/guide/isis-applib.xml
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/applib/src/docbkx/guide/isis-applib.xml?rev=1195624&r1=1195623&r2=1195624&view=diff
==============================================================================
--- incubator/isis/trunk/framework/applib/src/docbkx/guide/isis-applib.xml (original)
+++ incubator/isis/trunk/framework/applib/src/docbkx/guide/isis-applib.xml Mon Oct 31 19:11:30 2011
@@ -1154,23 +1154,49 @@
         <para>A title is used to identify an object to the user in the user
         interface. For example, a <classname>Customer</classname>'s title
         might be the organization's customer reference, or perhaps (more
-        informally) their first and last names. By default, the framework will
-        use the object's <literal moreinfo="none">toString()</literal> method
-        as the title. However, if a <literal moreinfo="none">title()
-        </literal>method (returning a <literal
-        moreinfo="none">String</literal>) is present, then that is used
-        instead, thus:</para>
+        informally) their first and last names.</para>
 
-        <para><programlisting>public String toString()</programlisting></para>
+        <para>By default, the framework will use the object's <literal
+        moreinfo="none">toString()</literal> method as the title. Most titles
+        tend to be made up of the same set of elements: for example a
+        Customer's name might be the concatenation of their customer first
+        name and their ;ast name. For these the <classname>@Title</classname>
+        annotation can be used:</para>
+
+        <para><programlisting>public class Customer {
+  @Title
+  public String getFirstName() { ... }
+  @Title
+  public String getLastName() { ... }
+  ...
+}</programlisting></para>
 
-        <para>or</para>
+        <para>For more control, the order of the title components can be
+        specified using a sequence number (specified in Dewey decimal
+        format):</para>
 
-        <para><programlisting>public String title()</programlisting></para>
+        <programlisting>public class Customer {
+  @Title("1.0")
+  public String getFirstName() { ... }
+  @Title("1.1")
+  public String getLastName() { ... }
+  ...
+}</programlisting>
 
-        <para>The reason for providing the option to use a <literal
-        moreinfo="none">title()</literal> method is in case the programmer
+        <para>For more control the title can be declared imperately using the
+        <literal moreinfo="none">title()</literal> method (returning a
+        <literal moreinfo="none">String</literal>). This leaves the programmer
         needs to make use of the <literal moreinfo="none">toString()</literal>
-        method for other purposes, such as for debugging.</para>
+        method for other purposes, such as for debugging. For example, to
+        return the title for a customer which is their last name and then
+        first initial of their first name, we could use:</para>
+
+        <para><programlisting>public class Customer {
+  public String title() {
+    return getLastName() + ", " + getFirstName().substring(0,1);
+  } 
+  ...
+}</programlisting></para>
 
         <para>The applib contains a class,
         <classname>org.apache.isis.applib.util.TitleBuffer</classname>, which
@@ -7053,6 +7079,33 @@ public class EmailAddress {
       </sect1>
 
       <sect1>
+        <title>@Title</title>
+
+        <para>The <literal moreinfo="none">@Title</literal> annotation is used
+        to indicate which property or properties make up the object title. If
+        more than one property is used, the order can be specified (using the
+        same Dewey-decimal notation as used by @MemberOrder) and the string to
+        use between the components can also be specified.</para>
+
+        <para>For example:</para>
+
+        <programlisting format="linespecific">public void Customer {
+    @Title(sequence="1.0")
+    public String lastName() { ... }
+    ...
+    @Title(sequence="1.5", prepend=", ")
+    public String firstName() { ... }
+    ...
+    @Title(sequence="1.7", append=".")
+    public String midInitial() { ... }
+    ...
+}</programlisting>
+
+        <para>could be used to create names of the style "Bloggs, Joe
+        K."</para>
+      </sect1>
+
+      <sect1>
         <title>@TypeOf</title>
 
         <para>The <literal moreinfo="none">@TypeOf</literal> annotation is

Copied: incubator/isis/trunk/framework/applib/src/main/java/org/apache/isis/applib/annotation/Title.java (from r1189396, incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/Title.java)
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/applib/src/main/java/org/apache/isis/applib/annotation/Title.java?p2=incubator/isis/trunk/framework/applib/src/main/java/org/apache/isis/applib/annotation/Title.java&p1=incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/Title.java&r1=1189396&r2=1195624&rev=1195624&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/Title.java (original)
+++ incubator/isis/trunk/framework/applib/src/main/java/org/apache/isis/applib/annotation/Title.java Mon Oct 31 19:11:30 2011
@@ -17,7 +17,7 @@
  *  under the License.
  */
 
-package org.apache.isis.core.progmodel.facets.object.title.annotation;
+package org.apache.isis.applib.annotation;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -30,4 +30,18 @@ import java.lang.annotation.Target;
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
-public @interface Title {}
+public @interface Title {
+    /**
+     * The order (in Dewey decimal notation) that the property annotated with {@link Title} appears with respect to 
+     * other properties also annotated with {@link Title}.
+     */
+    String sequence() default "1.0";
+    /**
+     * The string to use to separate this property from any preceding properties in the title.  
+     */
+    String prepend() default " ";
+    /**
+     * The string to append to this property if non-empty.  
+     */
+    String append() default "";
+}

Modified: incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java?rev=1195624&r1=1195623&r2=1195624&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java (original)
+++ incubator/isis/trunk/framework/core/metamodel/src/main/java/org/apache/isis/core/metamodel/layout/memberorderfacet/MemberOrderComparator.java Mon Oct 31 19:11:30 2011
@@ -91,16 +91,11 @@ public class MemberOrderComparator imple
             throw new IllegalArgumentException("Not in same group");
         }
 
-        final StringTokenizer tokens1 = new StringTokenizer(m1.sequence(), ".", false);
-        final String[] components1 = new String[tokens1.countTokens()];
-        for (int i = 0; tokens1.hasMoreTokens(); i++) {
-            components1[i] = tokens1.nextToken();
-        }
-        final StringTokenizer tokens2 = new StringTokenizer(m2.sequence(), ".", false);
-        final String[] components2 = new String[tokens2.countTokens()];
-        for (int i = 0; tokens2.hasMoreTokens(); i++) {
-            components2[i] = tokens2.nextToken();
-        }
+        final String sequence1 = m1.sequence();
+        final String sequence2 = m2.sequence();
+        
+        final String[] components1 = componentsFor(sequence1);
+        final String[] components2 = componentsFor(sequence2);
 
         final int length1 = components1.length;
         final int length2 = components2.length;
@@ -133,7 +128,7 @@ public class MemberOrderComparator imple
                 final Integer c2 = Integer.valueOf(components2[n]);
                 componentCompare = c1.compareTo(c2);
             } catch (final NumberFormatException nfe) {
-                // not integers compare as strings}
+                // not integers compare as strings
                 componentCompare = components1[n].compareTo(components2[n]);
             }
 
@@ -145,6 +140,15 @@ public class MemberOrderComparator imple
         }
     }
 
+    private static String[] componentsFor(final String sequence) {
+        final StringTokenizer tokens = new StringTokenizer(sequence, ".", false);
+        final String[] components = new String[tokens.countTokens()];
+        for (int i = 0; tokens.hasMoreTokens(); i++) {
+            components[i] = tokens.nextToken();
+        }
+        return components;
+    }
+
     private MemberOrderFacet getMemberOrder(final FacetHolder facetHolder) {
         return facetHolder.getFacet(MemberOrderFacet.class);
     }

Modified: incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java?rev=1195624&r1=1195623&r2=1195624&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java (original)
+++ incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleAnnotationFacetFactory.java Mon Oct 31 19:11:30 2011
@@ -20,8 +20,11 @@
 package org.apache.isis.core.progmodel.facets.object.title.annotation;
 
 import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
+import org.apache.isis.applib.annotation.Title;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facetapi.FacetUtil;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
@@ -29,6 +32,11 @@ import org.apache.isis.core.metamodel.fa
 import org.apache.isis.core.metamodel.methodutils.MethodScope;
 import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
 import org.apache.isis.core.progmodel.facets.fallback.FallbackFacetFactory;
+import org.apache.isis.core.progmodel.facets.object.title.annotation.TitleFacetViaTitleAnnotation.TitleComponent;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
 
 public class TitleAnnotationFacetFactory extends FacetFactoryAbstract {
 
@@ -44,15 +52,76 @@ public class TitleAnnotationFacetFactory
         final Class<?> cls = processClassContext.getCls();
         final FacetHolder facetHolder = processClassContext.getFacetHolder();
 
-        // TODO - MNour: Add method in MethodFinderUtils to find methods with a specified annotation. 
         List<Method> methods = MethodFinderUtils.findMethodsWithAnnotation(cls, MethodScope.OBJECT, Title.class);
 
-        if (!methods.isEmpty()) {
-	        for (Method method : methods) {
-	            processClassContext.removeMethod(method);
-	        }
-	        FacetUtil.addFacet(new TitleFacetViaTitleAnnotation(methods, facetHolder));
-	        return;
+        Collections.sort(methods, new Comparator<Method>() {
+            Comparator<String> comparator = new SequenceComparator();
+            @Override
+            public int compare(Method o1, Method o2) {
+                final Title a1 = o1.getAnnotation(Title.class);
+                final Title a2 = o2.getAnnotation(Title.class);
+                return comparator.compare(a1.sequence(), a2.sequence());
+            }
+        });
+        if (methods.isEmpty()) {
+            return;
+        } 
+        List<TitleComponent> titleComponents = Lists.transform(methods, TitleComponent.FROM_METHOD);
+        FacetUtil.addFacet(new TitleFacetViaTitleAnnotation(titleComponents, facetHolder));
+    }
+    
+    static class SequenceComparator implements Comparator<String> {
+
+        @Override
+        public int compare(String sequence1, String sequence2) {
+            
+            final List<String> components1 = componentsFor(sequence1);
+            final List<String> components2 = componentsFor(sequence2);
+
+            final int size1 = components1.size();
+            final int size2 = components2.size();
+
+            if (size1 == 0 && size2 == 0) {
+                return 0;
+            }
+
+            // continue to loop until we run out of components.
+            int n = 0;
+            while (true) {
+                final int length = n + 1;
+                // check if run out of components in either side
+                if (size1 < length && size2 >= length) {
+                    return -1; // o1 before o2
+                }
+                if (size2 < length && size1 >= length) {
+                    return +1; // o2 before o1
+                }
+                if (size1 < length && size2 < length) {
+                    // run out of components
+                    return 0;
+                }
+                // we have this component on each side
+                int componentCompare = 0;
+                try {
+                    final Integer c1 = Integer.valueOf(components1.get(n));
+                    final Integer c2 = Integer.valueOf(components2.get(n));
+                    componentCompare = c1.compareTo(c2);
+                } catch (final NumberFormatException nfe) {
+                    // not integers compare as strings
+                    componentCompare = components1.get(n).compareTo(components2.get(n));
+                }
+
+                if (componentCompare != 0) {
+                    return componentCompare;
+                }
+                // this component is the same; lets look at the next
+                n++;
+            }
+        }
+
+        private static List<String> componentsFor(String sequence) {
+            return Lists.newArrayList(Splitter.on('.').split(sequence));
         }
+        
     }
 }

Modified: incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java?rev=1195624&r1=1195623&r2=1195624&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java (original)
+++ incubator/isis/trunk/framework/core/progmodel/src/main/java/org/apache/isis/core/progmodel/facets/object/title/annotation/TitleFacetViaTitleAnnotation.java Mon Oct 31 19:11:30 2011
@@ -20,43 +20,62 @@
 package org.apache.isis.core.progmodel.facets.object.title.annotation;
 
 import java.lang.reflect.Method;
-import java.util.Collections;
 import java.util.List;
 
+import org.apache.isis.applib.annotation.Title;
 import org.apache.isis.applib.profiles.Localization;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
-import org.apache.isis.core.metamodel.facets.ImperativeFacet;
 import org.apache.isis.core.metamodel.facets.object.title.TitleFacetAbstract;
 import org.apache.log4j.Logger;
 
-public class TitleFacetViaTitleAnnotation extends TitleFacetAbstract implements ImperativeFacet {
+import com.google.common.base.Function;
+import com.google.common.base.Strings;
+
+public class TitleFacetViaTitleAnnotation extends TitleFacetAbstract  {
 
     private static final Logger LOG = Logger.getLogger(TitleFacetViaTitleAnnotation.class);
-    private final List<Method> methods;
+    private final List<TitleComponent> components;
 
-    public TitleFacetViaTitleAnnotation(final List<Method> methods, final FacetHolder holder) {
-        super(holder);
-        this.methods = methods;
-    }
+    public static class TitleComponent {
+        public static final Function<? super Method, ? extends TitleComponent> FROM_METHOD = new Function<Method, TitleComponent>() {
 
-    /**
-     * Returns a singleton list of the {@link Method}(s) provided in the constructor.
-     */
-    @Override
-    public List<Method> getMethods() {
-        return Collections.unmodifiableList(methods);
-    }
-
-    @Override
-    public boolean impliesResolve() {
-        return true;
+            @Override
+            public TitleComponent apply(Method input) {
+                return TitleComponent.of(input);
+            }
+        };
+
+
+        private final String prepend;
+        private final String append;
+        private final Method method;
+        private TitleComponent(String prepend, String append, Method method) {
+            super();
+            this.prepend = prepend;
+            this.append = append;
+            this.method = method;
+        }
+        public String getPrepend() {
+            return prepend;
+        }
+        public String getAppend() {
+            return append;
+        }
+        public Method getMethod() {
+            return method;
+        }
+        public static TitleComponent of(Method method) {
+            final Title annotation = method.getAnnotation(Title.class);
+            final String prepend = annotation!=null?annotation.prepend():" ";
+            final String append = annotation!=null?annotation.append():"";
+            return new TitleComponent(prepend, append, method);
+        }
     }
-
-    @Override
-    public boolean impliesObjectChanged() {
-        return false;
+    public TitleFacetViaTitleAnnotation(final List<TitleComponent> components, final FacetHolder holder) {
+        super(holder);
+        this.components = components;
     }
 
     @Override
@@ -64,8 +83,20 @@ public class TitleFacetViaTitleAnnotatio
     	StringBuilder stringBuilder = new StringBuilder();
 
         try {
-        	for (Method method : this.methods) {
-        		stringBuilder.append((String) AdapterInvokeUtils.invoke(method, owningAdapter)).append(' ');
+        	for (TitleComponent entry : this.components) {
+        		final Object titlePart = AdapterInvokeUtils.invoke(entry.getMethod(), owningAdapter);
+        		if(titlePart == null) {
+                    continue;
+                }
+        		final String trim = titlePart.toString().trim();
+                if (Strings.isNullOrEmpty(trim)) {
+                    continue;
+                }
+                if(stringBuilder.length() > 0) {
+                    stringBuilder.append(entry.getPrepend());
+                }
+                stringBuilder.append(trim);
+                stringBuilder.append(entry.getAppend());
         	}
 
         	return stringBuilder.toString().trim();
@@ -75,4 +106,8 @@ public class TitleFacetViaTitleAnnotatio
         }
     }
 
+    public List<TitleComponent> getComponents() {
+        return components;
+    }
+
 }

Modified: incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java?rev=1195624&r1=1195623&r2=1195624&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java (original)
+++ incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleAnnotationFacetFactoryTest.java Mon Oct 31 19:11:30 2011
@@ -16,105 +16,211 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
-
 package org.apache.isis.core.progmodel.facets.object.ident.title.annotation;
 
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertThat;
+
 import java.lang.reflect.Method;
+import java.util.Arrays;
 import java.util.List;
 
+import org.apache.isis.applib.annotation.Title;
+import org.apache.isis.applib.profiles.Localization;
+import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.facetapi.Facet;
 import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessClassContext;
 import org.apache.isis.core.metamodel.facets.object.title.TitleFacet;
-import org.apache.isis.core.metamodel.methodutils.MethodScope;
 import org.apache.isis.core.progmodel.facets.AbstractFacetFactoryTest;
-import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
-import org.apache.isis.core.progmodel.facets.object.title.annotation.Title;
 import org.apache.isis.core.progmodel.facets.object.title.annotation.TitleAnnotationFacetFactory;
 import org.apache.isis.core.progmodel.facets.object.title.annotation.TitleFacetViaTitleAnnotation;
+import org.apache.isis.core.progmodel.facets.object.title.annotation.TitleFacetViaTitleAnnotation.TitleComponent;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-
+
+@RunWith(JMock.class)
 public class TitleAnnotationFacetFactoryTest extends AbstractFacetFactoryTest {
+
+    private Mockery context = new JUnit4Mockery();
+    private ObjectAdapter objectAdapter;
 
     private TitleAnnotationFacetFactory facetFactory;
-
+
+    @Before
     @Override
-    protected void setUp() throws Exception {
+    public void setUp() throws Exception {
         super.setUp();
-
+
+        objectAdapter = context.mock(ObjectAdapter.class);
+                
         facetFactory = new TitleAnnotationFacetFactory();
     }
-
+
+    @After
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         facetFactory = null;
         super.tearDown();
     }
 
-    public void testTitleAnnotatedMethodPickedUpOnClassAndMethodRemoved() {
-        class Customer {
-
-            @SuppressWarnings("unused")
-            @Title
-            public String someTitle() {
-                return "Some Title";
-            }
-
+    public static class Customer {
+
+        @SuppressWarnings("unused")
+        @Title
+        public String someTitle() {
+            return "Some Title";
         }
+    }
 
-        titleAnnotatedMethodsPickedUpOnClassAndMethodsRemovedCommonTest(Customer.class);
-    }
 
-    public void testTitleAnnotatedMethodsPickedUpOnClassAndMethodsRemoved() {
-        class Customer {
+    @Test
+    public void testTitleAnnotatedMethodPickedUpOnClassRemoved() throws Exception {
+        facetFactory.process(new ProcessClassContext(Customer.class, methodRemover, facetedMethod));
+        
+        final Facet facet = facetedMethod.getFacet(TitleFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof TitleFacetViaTitleAnnotation);
+        final TitleFacetViaTitleAnnotation titleFacetViaTitleAnnotation = (TitleFacetViaTitleAnnotation) facet;
+        
+        final List<Method> titleMethods = Arrays.asList(Customer.class.getMethod("someTitle"));
+        for(int i=0; i<titleMethods.size(); i++) {
+            assertEquals(titleMethods.get(i), titleFacetViaTitleAnnotation.getComponents().get(i).getMethod());
+        }
+    }
 
-            @SuppressWarnings("unused")
-            @Title
-            public String titleElement1() {
-                return "titleElement1";
-            }
+    public static class Customer2 {
 
-            @SuppressWarnings("unused")
-            @Title
-            public String titleElement2() {
-                return "titleElement2";
-            }
+        @Title(sequence = "1", append=".")
+        public String titleElement1() {
+            return "titleElement1";
+        }
 
-            @SuppressWarnings("unused")
-            @Title
-            public String titleElement3() {
-                return "titleElement3";
-            }
+        @Title(sequence = "2", prepend=",")
+        public String titleElement2() {
+            return "titleElement2";
+        }
 
+        @Title(sequence = "1.5")
+        public String titleElement3() {
+            return "titleElement3";
         }
 
-        titleAnnotatedMethodsPickedUpOnClassAndMethodsRemovedCommonTest(Customer.class);
     }
 
+    @Test
+    public void testTitleAnnotatedMethodsPickedUpOnClass() throws Exception {
+
+        facetFactory.process(new ProcessClassContext(Customer2.class, methodRemover, facetedMethod));
+        
+        final Facet facet = facetedMethod.getFacet(TitleFacet.class);
+        assertNotNull(facet);
+        assertTrue(facet instanceof TitleFacetViaTitleAnnotation);
+        final TitleFacetViaTitleAnnotation titleFacetViaTitleAnnotation = (TitleFacetViaTitleAnnotation) facet;
+        
+        final List<Method> titleMethods = Arrays.asList(
+                Customer2.class.getMethod("titleElement1"), Customer2.class.getMethod("titleElement3"), Customer2.class.getMethod("titleElement2"));
+
+        final List<TitleComponent> components = titleFacetViaTitleAnnotation.getComponents();
+        for(int i=0; i<titleMethods.size(); i++) {
+            assertEquals(titleMethods.get(i), components.get(i).getMethod());
+        }
+        
+        final Customer2 customer = new Customer2();
+        
+        Localization localization = null;
+        context.checking(new Expectations() {
+            {
+                allowing(objectAdapter).getObject();
+                will(returnValue(customer));
+            }
+        });
+        final String title = titleFacetViaTitleAnnotation.title(objectAdapter, localization );
+        assertThat(title, is("titleElement1. titleElement3,titleElement2"));
+    }
+    
+    
+    public static class Customer3 {}
+
+    @Test
     public void testNoExplicitTitleAnnotations() {
-        class Customer {}
 
-        facetFactory.process(new ProcessClassContext(Customer.class, methodRemover, facetedMethod));
+        facetFactory.process(new ProcessClassContext(Customer3.class, methodRemover, facetedMethod));
 
         assertNull(facetedMethod.getFacet(TitleFacet.class));
-
-        assertNoMethodsRemoved();
     }
 
-    protected void titleAnnotatedMethodsPickedUpOnClassAndMethodsRemovedCommonTest(Class<?> type) {
-    	assertNotNull("Type MUST not be 'null'", type);
+    
+    public static class Customer4 {
+
+        @Title(sequence = "1")
+        public String titleElement1() {
+            return "titleElement1";
+        }
+
+        @Title(sequence = "2")
+        public String titleElement2() {
+            return null;
+        }
 
-        final List<Method> titleMethods = MethodFinderUtils.findMethodsWithAnnotation(type, MethodScope.OBJECT, Title.class);
+        @Title(sequence = "3")
+        public String titleElement3() {
+            return "titleElement3";
+        }
 
-        facetFactory.process(new ProcessClassContext(type, methodRemover, facetedMethod));
+        @Title(sequence = "4", prepend="ignored-since-null", append="ignored-since-null")
+        public Object titleElement4() {
+            return null;
+        }
 
+        @Title(sequence = "4.4", prepend="ignored-since-empty-string", append="ignored-since-empty-string")
+        public Object titleElement4a() {
+            return "";
+        }
+
+        @Title(sequence = "5")
+        public String titleElement5() {
+            return "titleElement5";
+        }
+
+        @Title(sequence = "6")
+        public Integer titleElement6() {
+            return 3;
+        }
+
+        @Title(sequence = "7")
+        public String titleElement7() {
+            return "  this needs to be trimmed      ";
+        }
+
+
+    }
+
+    @Test
+    public void tTitleAnnotatedMethodsSomeOfWhichReturnNulls() throws Exception {
+
+        facetFactory.process(new ProcessClassContext(Customer4.class, methodRemover, facetedMethod));
+        
         final Facet facet = facetedMethod.getFacet(TitleFacet.class);
-        assertNotNull(facet);
-        assertTrue(facet instanceof TitleFacetViaTitleAnnotation);
         final TitleFacetViaTitleAnnotation titleFacetViaTitleAnnotation = (TitleFacetViaTitleAnnotation) facet;
-        assertEquals(titleMethods, titleFacetViaTitleAnnotation.getMethods());
-
-        assertTrue(methodRemover.getRemovedMethodMethodCalls().containsAll(titleMethods));
+        
+        final Customer4 customer = new Customer4();
+        
+        Localization localization = null;
+        context.checking(new Expectations() {
+            {
+                allowing(objectAdapter).getObject();
+                will(returnValue(customer));
+            }
+        });
+        final String title = titleFacetViaTitleAnnotation.title(objectAdapter, localization );
+        assertThat(title, is("titleElement1 titleElement3 titleElement5 3 this needs to be trimmed"));
     }
 
 }

Modified: incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleFacetViaTitleAnnotationTest.java
URL: http://svn.apache.org/viewvc/incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleFacetViaTitleAnnotationTest.java?rev=1195624&r1=1195623&r2=1195624&view=diff
==============================================================================
--- incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleFacetViaTitleAnnotationTest.java (original)
+++ incubator/isis/trunk/framework/core/progmodel/src/test/java/org/apache/isis/core/progmodel/facets/object/ident/title/annotation/TitleFacetViaTitleAnnotationTest.java Mon Oct 31 19:11:30 2011
@@ -24,14 +24,16 @@ import static org.hamcrest.CoreMatchers.
 import static org.junit.Assert.assertThat;
 
 import java.lang.reflect.Method;
+import java.util.Arrays;
 import java.util.List;
 
+import org.apache.isis.applib.annotation.Title;
 import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.methodutils.MethodScope;
 import org.apache.isis.core.progmodel.facets.MethodFinderUtils;
-import org.apache.isis.core.progmodel.facets.object.title.annotation.Title;
 import org.apache.isis.core.progmodel.facets.object.title.annotation.TitleFacetViaTitleAnnotation;
+import org.apache.isis.core.progmodel.facets.object.title.annotation.TitleFacetViaTitleAnnotation.TitleComponent;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
 import org.jmock.integration.junit4.JMock;
@@ -39,6 +41,8 @@ import org.jmock.integration.junit4.JUni
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+
+import com.google.common.collect.Lists;
 
 @RunWith(JMock.class)
 public class TitleFacetViaTitleAnnotationTest  {
@@ -58,17 +62,17 @@ public class TitleFacetViaTitleAnnotatio
 
     protected static class NormalDomainObject {
 
-    	@Title
+    	@Title(sequence="1.0")
         public String titleElement1() {
             return "Normal";
         }
 
-    	@Title
+    	@Title(sequence="2.0")
         public String titleElement2() {
             return "Domain";
         }
 
-    	@Title
+    	@Title(sequence="3.0")
         public String titleElement3() {
             return "Object";
         }
@@ -82,11 +86,11 @@ public class TitleFacetViaTitleAnnotatio
     }
 
     @Test
-    public void testTitle() {
-        List<Method> methods = MethodFinderUtils.findMethodsWithAnnotation(NormalDomainObject.class, MethodScope.OBJECT,
-        		Title.class);
+    public void testTitle() throws Exception {
+        List<Method> methods = Arrays.asList(NormalDomainObject.class.getMethod("titleElement1"), NormalDomainObject.class.getMethod("titleElement2"), NormalDomainObject.class.getMethod("titleElement3"));
 
-        TitleFacetViaTitleAnnotation facet = new TitleFacetViaTitleAnnotation(methods, mockFacetHolder);
+        final List<TitleComponent> components = Lists.transform(methods, TitleComponent.FROM_METHOD);
+        TitleFacetViaTitleAnnotation facet = new TitleFacetViaTitleAnnotation(components, mockFacetHolder);
     	final NormalDomainObject normalPojo = new NormalDomainObject();
 		mockery.checking(new Expectations(){{
 			allowing(mockOwningAdapter).getObject();
@@ -102,7 +106,8 @@ public class TitleFacetViaTitleAnnotatio
         List<Method> methods = MethodFinderUtils.findMethodsWithAnnotation(DomainObjectWithProblemInItsAnnotatedTitleMethod.class, MethodScope.OBJECT,
         		Title.class);
 
-        TitleFacetViaTitleAnnotation facet = new TitleFacetViaTitleAnnotation(methods, mockFacetHolder);
+        final List<TitleComponent> components = Lists.transform(methods, TitleComponent.FROM_METHOD);
+        TitleFacetViaTitleAnnotation facet = new TitleFacetViaTitleAnnotation(components, mockFacetHolder);
     	final DomainObjectWithProblemInItsAnnotatedTitleMethod screwedPojo = new DomainObjectWithProblemInItsAnnotatedTitleMethod();
 		mockery.checking(new Expectations(){{
 			allowing(mockOwningAdapter).getObject();