Commits

Dimitris Zenios committed 618be60

More work on the breadcrumbs module

Comments (0)

Files changed (22)

 	<groupId>com.zenios</groupId>
 	<artifactId>tapestry-zbreadcrumbs</artifactId>
 	<version>0.0.1-SNAPSHOT</version>
-	
-  <dependencies>
-	<dependency>
-		<groupId>org.apache.tapestry</groupId>
-		<artifactId>tapestry-core</artifactId>
-		<version>${tapestry-release-version}</version>
-	</dependency>
-	
-  </dependencies>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.tapestry</groupId>
+			<artifactId>tapestry-core</artifactId>
+			<version>${tapestry-release-version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.tapestry</groupId>
+			<artifactId>tapestry-ioc</artifactId>
+			<version>${tapestry-release-version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>servlet-api</artifactId>
+			<version>2.5</version>
+			<scope>provided</scope>
+		</dependency>
+
+	</dependencies>
+
+	<build>
+		<resources>
+			<resource>
+				<directory>src/main/resources</directory>
+			</resource>
+		</resources>
+
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>2.4</version>
+				<configuration>
+					<archive>
+						<manifestEntries>
+							<Tapestry-Module-Classes>com.zenios.tapestry.breadcrumbs.services.BreadCrumbsModule</Tapestry-Module-Classes>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+
+		</plugins>
+	</build>
 
 	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<tapestry-release-version>5.3.3</tapestry-release-version>
 	</properties>
 </project>

src/main/java/com/zenios/tapestry/breadcrumbs/annotation/BreadCrumb.java

 public @interface BreadCrumb {
 	boolean ignore() default false;
 	boolean reset() default false;
-	boolean titleReset() default false;
+	boolean dynamicTitle() default false;
 }

src/main/java/com/zenios/tapestry/breadcrumbs/components/BreadCrumbInfo.java

 package com.zenios.tapestry.breadcrumbs.components;
 
+import org.apache.tapestry5.BindingConstants;
+import org.apache.tapestry5.Block;
+import org.apache.tapestry5.ComponentEventCallback;
+import org.apache.tapestry5.EventContext;
 import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.internal.util.Holder;
+import org.apache.tapestry5.ioc.Messages;
 import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.ioc.internal.util.TapestryException;
+import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.services.ComponentSource;
 
 import com.zenios.tapestry.breadcrumbs.other.BreadCrumbObject;
-import com.zenios.tapestry.breadcrumbs.utils.BreadCrumbUtils;
+import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsConstants;
+import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsEvents;
 
-public class BreadCrumbInfo {
+public class BreadCrumbInfo {   
+    @Inject
+    private ComponentSource componentSource;
+            
+    @Property
     @Parameter(required = true, principal = true, autoconnect = true)
     private BreadCrumbObject crumb;
     
+    @Parameter(value = "block:before",defaultPrefix = BindingConstants.LITERAL)
+    private Block before;
+    
+    @Parameter(value = "block:after",defaultPrefix = BindingConstants.LITERAL)
+    private Block after;
+    
+    @SuppressWarnings("unused")
+	@Property
+    @Parameter(value = "block:title",defaultPrefix = BindingConstants.LITERAL)
+    private Block title;
+    
     @Inject
-    private ComponentSource componentSource;
+    @Symbol(BreadCrumbsConstants.BREADCRUMBS_TITLE_SUFFIX)
+    private String titleSuffix;
+    
+    Object beginRender()
+    {
+    	return before;
+    }
+    
+    Object afterRender()
+    {
+    	return after;
+    }
         
+   
+    public String getNameTitle() {
+		String pageName = crumb.getLogicalPageName();
+		Component page = componentSource.getPage(pageName);
+		String title = null;
+		if(crumb.isTitleEvent()) {
+			final Holder<String> valueHolder = Holder.create();
+			ComponentEventCallback<String> callback = new ComponentEventCallback<String>() {
+				public boolean handleResult(String result)
+				{
+					valueHolder.put(result);
+	
+					return true;
+				}
+			};
+
+
+			boolean handled = page.getComponentResources().triggerContextEvent(BreadCrumbsEvents.GET_TITLE, crumb.getActivationContext(), callback);
+			if (!handled) {
+				throw new TapestryException(String.format("Request event '%s' (on component %s) was not handled; you must provide a matching event handler method in the component or in one of its containers.", BreadCrumbsEvents.GET_TITLE, page.getComponentResources().getCompleteId()), page, null);
+			}
+			title = valueHolder.get();
+		}
+			
+		if(title == null) {
+			String key = new StringBuilder(pageName).append(titleSuffix).toString();
+			Messages messages = page.getComponentResources().getMessages();
+			if(messages.contains(key)) {
+				title = messages.get(key);
+			}
+		}
+		
+		return title != null ? title : pageName;
+    }
+    
     public String getPage() {
-    	return crumb.getParams().getLogicalPageName();
+    	return componentSource.getPage(crumb.getLogicalPageName()).getComponentResources().getCompleteId();
     }
     
-    public String getTitle() {
-    	String title = crumb.getTitle();
-    	
-    	if(title == null) {
-    		title = BreadCrumbUtils.getTitle(componentSource.getPage(crumb.getParams().getLogicalPageName()), crumb.getParams());
-    	}
-    	return title;
-    	
+    public Object[] getActivationContext() {
+    	EventContext context = crumb.getActivationContext();
+        int count = context.getCount();
+
+        Object[] pageActivationContext = new Object[count];
+
+        for(int i = 0; i < count; i++)
+            pageActivationContext[i] = context.get(Object.class, i);
+        
+        return pageActivationContext; 
     }
 }

src/main/java/com/zenios/tapestry/breadcrumbs/components/BreadCrumbsLoop.java

 import org.apache.tapestry5.annotations.Property;
 import org.apache.tapestry5.grid.GridConstants;
 import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.ioc.annotations.Inject;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.services.ComponentSource;
 
 import com.zenios.tapestry.breadcrumbs.other.BreadCrumbObject;
 import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsModel;
+import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsTrailConstantsValues;
 
 @SuppressWarnings("unused")
 public class BreadCrumbsLoop {
     
    	@Parameter
     private int rowIndex;
-        
+   	        
     @Parameter(value = "componentResources.container")
     private BreadCrumbsModel breadCrumbsModel;
     
     @Parameter(required = true)
     @Property(write = false)
     private BreadCrumbObject row;
+    
+    @Inject
+    private ComponentSource componentSource;
     	
-    @Component(parameters="crumb=row")
+    @Component(parameters="crumb=row", publishParameters = "before,after,title")
     private BreadCrumbInfo breadCrumb;
     
     private int dataRowIndex;
     {
         List<String> classes = CollectionFactory.newList();
 
-        // Not a cached parameter, so careful to only access it once.
-
         String rc = rowClass;
 
         if (rc != null) classes.add(rc);
 
-        if (dataRowIndex == startRow) classes.add(GridConstants.FIRST_CLASS);
-
-        if (dataRowIndex == endRow) classes.add(GridConstants.LAST_CLASS);
+        if (dataRowIndex == startRow) classes.add(BreadCrumbsTrailConstantsValues.BREADCRUMBS_FIRST_CLASS);
 
+        if (dataRowIndex == endRow) classes.add(BreadCrumbsTrailConstantsValues.BREADCRUMBS_LAST_CLASS);
+        
+        if(componentSource.getActivePage().getComponentResources().getCompleteId().equals(componentSource.getPage(row.getLogicalPageName()).getComponentResources().getCompleteId())) {
+        	classes.add(BreadCrumbsTrailConstantsValues.BREADCRUMBS_ACTIVE_CLASS);
+        }
+        
         return TapestryInternalUtils.toClassAttributeValue(classes);
     }
     
 
         return row != null;
     }
-
+    
     boolean afterRender()
     {
         dataRowIndex++;

src/main/java/com/zenios/tapestry/breadcrumbs/components/BreadCrumbsTrail.java

 	@SessionState
 	private BreadCrumbsList breadCrumbs;
 	
-    @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL,value = BindingConstants.SYMBOL + ":" + BreadCrumbsConstants.BREADCRUMB_TRAIL_CSS_CLASS)
+    @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL,value = BindingConstants.SYMBOL + ":" + BreadCrumbsConstants.BREADCRUMBS_TRAIL_CSS_CLASS)
     @Property(write = false)
     private String trailClass;
     
     @Property
     private Any trail;
     
-    @Component(parameters ="row=row" , publishParameters = "rowIndex,rowClass")
+    @Component(parameters ="row=row" , publishParameters = "rowIndex,rowClass,before,after,title")
     @Property
     private BreadCrumbsLoop trailCrumbs;
-    
-    @Parameter(value = "block:empty",defaultPrefix = BindingConstants.LITERAL)
-    private Block empty;
-    
+        
     @Parameter(principal = true)
     private BreadCrumbObject row;
     
 
     private CachingBreadCrumbSource cachingSource;
     
-    Object setupRender()
+    boolean setupRender()
     {
     	cachingSource = new CachingBreadCrumbSource(breadCrumbs.getBreadCrumbs());
     	
-        return cachingSource.getAvailableRows() == 0 ? empty : null;
+        return cachingSource.getAvailableRows() == 0 ? false : true;
     }
     
     static class CachingBreadCrumbSource

src/main/java/com/zenios/tapestry/breadcrumbs/other/BreadCrumbObject.java

 package com.zenios.tapestry.breadcrumbs.other;
 
-import org.apache.tapestry5.services.PageRenderRequestParameters;
+import org.apache.tapestry5.EventContext;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
 
 public class BreadCrumbObject {
-	private final PageRenderRequestParameters params;
-	private final String title;
+	private final String logicalPageName;
+	private final EventContext activationContext;
+	private final boolean titleEvent;
 	
-	public BreadCrumbObject(PageRenderRequestParameters params,String title) {
-		this.params = params;
-		this.title = title;
+	public BreadCrumbObject(String logicalPageName,EventContext activationContext,boolean titleEvent) {
+		this.logicalPageName = logicalPageName;
+		this.activationContext = activationContext;
+		this.titleEvent = titleEvent;
 	}
-
-	public PageRenderRequestParameters getParams() {
-		return params;
+	
+    public String getLogicalPageName() {
+		return logicalPageName;
 	}
-
-	public String getTitle() {
-		return title;
+    
+	public EventContext getActivationContext() {
+		return activationContext;
 	}
-
-    public boolean equals(BreadCrumbObject obj)
-    {
+	
+	public boolean isTitleEvent() {
+		return titleEvent;
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
         if (this == obj)
             return true;
 
         if (obj == null || getClass() != obj.getClass())
             return false;
+        
+        BreadCrumbObject other = (BreadCrumbObject)obj;
+        return TapestryInternalUtils.isEqual(activationContext, other.getActivationContext()) && 
+        		other.getLogicalPageName().equals(this.logicalPageName) &&
+        		other.isTitleEvent() == this.titleEvent;
+	}
 
-        return obj.getParams().equals(this.params) && obj.getTitle().equals(this.title);
-    }
 }

src/main/java/com/zenios/tapestry/breadcrumbs/other/BreadCrumbsConstants.java

 package com.zenios.tapestry.breadcrumbs.other;
 
 public class BreadCrumbsConstants {
-	public static final String BREADCRUMBS_FILTER = "breadcrumbs";
-	public static final String MAX_CRUMBS_TO_SAVE = "breadcrumbs-to-save";
-	public static final String DISCARD_DUPLICATES = "breadcrumbs-discrd-duplicates";
-    public static final String BREADCRUMB_TRAIL_CSS_CLASS = "tapestry-zbreadcrumbs.css_class";
+	public static final String BREADCRUMBS_FILTER = "zbreadcrumbs";
+	public static final String BREADCRUMBS_MAX_CRUMBS_TO_SAVE = "zbreadcrumbs-to-save";
+	public static final String BREADCRUMBS_DISCARD_DUPLICATES = "zbreadcrumbs-discrd-duplicates";
+    public static final String BREADCRUMBS_TRAIL_CSS_CLASS = "zbreadcrumbs-css_class";
+    public static final String BREADCRUMBS_TITLE_SUFFIX = "zbreadcrumbs-title-suffix";
+    public static final String BREADCRUMBS_DEFAULT_STYLESHEET = "zbreadcrumbs-default-stylesheet";
 }

src/main/java/com/zenios/tapestry/breadcrumbs/other/BreadCrumbsConstantsValues.java

+package com.zenios.tapestry.breadcrumbs.other;
+
+public class BreadCrumbsConstantsValues {
+    public static final String BREADCRUMBS_MAX_CRUMBS_TO_SAVE_VALUE = "10";
+    public static final String BREADCRUMB_TRAIL_CSS_CLASS_VALUE = "t-zbreadcrumbs";
+    public static final String BREADCRUMBS_DISCARD_DUPLICATES_VALUE = "true";
+    public static final String BREADCRUMBS_TITLE_SUFFIX_VALUE = "-crumb";
+    public static final String BREADCRUMBS_DEFAULT_STYLESHEET_VALUE = "classpath:/com/zenios/tapestry/breadcrumbs/Assets/default.css";
+}

src/main/java/com/zenios/tapestry/breadcrumbs/other/BreadCrumbsFilter.java

 package com.zenios.tapestry.breadcrumbs.other;
 
-public enum BreadCrumbsFilter {
-	RESET,
-	IGNORE,
-	TITLE_CHANGE
+public class BreadCrumbsFilter
+{
+	private final String pattern;
+	private final BreadCrumbsFilterType filterType;
+	
+	public BreadCrumbsFilter(String pattern, BreadCrumbsFilterType filterType) {
+		this.pattern = pattern;
+		this.filterType = filterType;
+	}
+
+	public String getPattern() {
+		return pattern;
+	}
+
+	public BreadCrumbsFilterType getFilterType() {
+		return filterType;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((filterType == null) ? 0 : filterType.hashCode());
+		result = prime * result + ((pattern == null) ? 0 : pattern.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		BreadCrumbsFilter other = (BreadCrumbsFilter) obj;
+		if (filterType != other.filterType)
+			return false;
+		if (pattern == null) {
+			if (other.pattern != null)
+				return false;
+		} else if (!pattern.equals(other.pattern))
+			return false;
+		return true;
+	}
+	
+
+	
 }

src/main/java/com/zenios/tapestry/breadcrumbs/other/BreadCrumbsFilterType.java

+package com.zenios.tapestry.breadcrumbs.other;
+
+public enum BreadCrumbsFilterType {
+	RESET,
+	IGNORE,
+	DYNAMIC_TITLE
+}

src/main/java/com/zenios/tapestry/breadcrumbs/other/BreadCrumbsTrailConstants.java

-package com.zenios.tapestry.breadcrumbs.other;
-
-public class BreadCrumbsTrailConstants {
-    public static final String TRAIL_CLASS = "t-zbreadcrumbs";
-}

src/main/java/com/zenios/tapestry/breadcrumbs/other/BreadCrumbsTrailConstantsValues.java

+package com.zenios.tapestry.breadcrumbs.other;
+
+public class BreadCrumbsTrailConstantsValues {
+	public static final String BREADCRUMBS_FIRST_CLASS = "z-first";
+	public static final String BREADCRUMBS_LAST_CLASS = "z-last";
+	public static final String BREADCRUMBS_ACTIVE_CLASS = "z-active";
+}

src/main/java/com/zenios/tapestry/breadcrumbs/services/BreadCrumbsModule.java

 package com.zenios.tapestry.breadcrumbs.services;
 
+import org.apache.tapestry5.Asset;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.Path;
+import org.apache.tapestry5.internal.services.DocumentLinker;
 import org.apache.tapestry5.ioc.Configuration;
 import org.apache.tapestry5.ioc.MappedConfiguration;
 import org.apache.tapestry5.ioc.OrderedConfiguration;
 import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.services.ApplicationStateContribution;
 import org.apache.tapestry5.services.ApplicationStateCreator;
+import org.apache.tapestry5.services.Environment;
 import org.apache.tapestry5.services.LibraryMapping;
+import org.apache.tapestry5.services.MarkupRenderer;
+import org.apache.tapestry5.services.MarkupRendererFilter;
 import org.apache.tapestry5.services.PageRenderRequestFilter;
+import org.apache.tapestry5.services.javascript.StylesheetLink;
 
 import com.zenios.tapestry.breadcrumbs.filter.BreadcrumbsPageRenderRequestFilter;
 import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsConstants;
-import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsTrailConstants;
+import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsConstantsValues;
 import com.zenios.tapestry.breadcrumbs.services.impl.BreadCrumbsServiceImpl;
 import com.zenios.tapestry.breadcrumbs.session.BreadCrumbsList;
-import com.zenios.tapestry.breadcrumbs.session.BreadCrumbsListImpl;
+import com.zenios.tapestry.breadcrumbs.session.impl.BreadCrumbsListImpl;
 
 public class BreadCrumbsModule {
 	
 	 }
 	
     public static void contributeFactoryDefaults(final MappedConfiguration<String, String> configuration) {
-        configuration.add(BreadCrumbsConstants.MAX_CRUMBS_TO_SAVE, "10");
-        configuration.add(BreadCrumbsConstants.DISCARD_DUPLICATES, "false");
-        configuration.add(BreadCrumbsConstants.BREADCRUMB_TRAIL_CSS_CLASS, BreadCrumbsTrailConstants.TRAIL_CLASS);
+        configuration.add(BreadCrumbsConstants.BREADCRUMBS_MAX_CRUMBS_TO_SAVE, BreadCrumbsConstantsValues.BREADCRUMBS_MAX_CRUMBS_TO_SAVE_VALUE);
+        configuration.add(BreadCrumbsConstants.BREADCRUMBS_DISCARD_DUPLICATES, BreadCrumbsConstantsValues.BREADCRUMBS_DISCARD_DUPLICATES_VALUE);
+        configuration.add(BreadCrumbsConstants.BREADCRUMBS_TRAIL_CSS_CLASS, BreadCrumbsConstantsValues.BREADCRUMB_TRAIL_CSS_CLASS_VALUE);
+        configuration.add(BreadCrumbsConstants.BREADCRUMBS_TITLE_SUFFIX, BreadCrumbsConstantsValues.BREADCRUMBS_TITLE_SUFFIX_VALUE);
+        configuration.add(BreadCrumbsConstants.BREADCRUMBS_DEFAULT_STYLESHEET, BreadCrumbsConstantsValues.BREADCRUMBS_DEFAULT_STYLESHEET_VALUE);
     }
     
 	public static void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration) {
 		configuration.addInstance(BreadCrumbsConstants.BREADCRUMBS_FILTER, BreadcrumbsPageRenderRequestFilter.class);
 	}
 	
-	public static void contributeApplicationStateManager(MappedConfiguration<Class<?>, ApplicationStateContribution> configuration,@Symbol(BreadCrumbsConstants.MAX_CRUMBS_TO_SAVE) final int crumbsToSave,@Symbol(BreadCrumbsConstants.DISCARD_DUPLICATES) final boolean discardDuplicates) {
+    public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,final Environment environment,@Path("${zbreadcrumbs-default-stylesheet}") final Asset defaultStylesheet) {
+        MarkupRendererFilter injectDefaultStylesheet = new MarkupRendererFilter()
+        {
+            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
+            {
+                DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
+
+                linker.addStylesheetLink(new StylesheetLink(defaultStylesheet.toClientURL()));
+
+                renderer.renderMarkup(writer);
+            }
+        };
+        
+        configuration.add("ZBreadCrumbsDefaultStylesheet", injectDefaultStylesheet, "after:InjectDefaultStylesheet");
+    }
+	
+	public static void contributeApplicationStateManager(MappedConfiguration<Class<?>, ApplicationStateContribution> configuration,@Symbol(BreadCrumbsConstants.BREADCRUMBS_MAX_CRUMBS_TO_SAVE) final int crumbsToSave,@Symbol(BreadCrumbsConstants.BREADCRUMBS_DISCARD_DUPLICATES) final boolean discardDuplicates) {
 		ApplicationStateCreator<BreadCrumbsList> breadCrumbsListCreator = new ApplicationStateCreator<BreadCrumbsList>()
 		{
 			public BreadCrumbsList create() {

src/main/java/com/zenios/tapestry/breadcrumbs/services/BreadCrumbsService.java

 package com.zenios.tapestry.breadcrumbs.services;
 
-import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+import org.apache.tapestry5.ioc.annotations.UsesConfiguration;
 import org.apache.tapestry5.services.PageRenderRequestParameters;
 
 import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsFilter;
 
-@UsesMappedConfiguration(BreadCrumbsFilter.class)
+@UsesConfiguration(BreadCrumbsFilter.class)
 public interface BreadCrumbsService {
 	public void addToBreadCrumbs(PageRenderRequestParameters params);
 }

src/main/java/com/zenios/tapestry/breadcrumbs/services/impl/BreadCrumbsServiceImpl.java

 package com.zenios.tapestry.breadcrumbs.services.impl;
 
 import java.lang.annotation.Annotation;
-import java.util.Map;
+import java.util.Collection;
+import java.util.Iterator;
 
 import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.services.ApplicationStateManager;
 import com.zenios.tapestry.breadcrumbs.annotation.BreadCrumb;
 import com.zenios.tapestry.breadcrumbs.other.BreadCrumbObject;
 import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsFilter;
+import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsFilterType;
 import com.zenios.tapestry.breadcrumbs.services.BreadCrumbsService;
 import com.zenios.tapestry.breadcrumbs.session.BreadCrumbsList;
-import com.zenios.tapestry.breadcrumbs.session.BreadCrumbsListImpl;
+import com.zenios.tapestry.breadcrumbs.session.impl.BreadCrumbsListImpl;
 import com.zenios.tapestry.breadcrumbs.utils.AntPathMatcher;
-import com.zenios.tapestry.breadcrumbs.utils.BreadCrumbUtils;
 import com.zenios.tapestry.breadcrumbs.utils.PatternMatcher;
 
 public class BreadCrumbsServiceImpl implements BreadCrumbsService {
 	
 	private final ApplicationStateManager applicationStateManager;
 	private final PatternMatcher patternMatcher;
-	private final Map<String,BreadCrumbsFilter> pathFilters;
+	private final Collection<BreadCrumbsFilter> pathFilters;
 	private final ComponentSource componentSource;
 	
-	public BreadCrumbsServiceImpl(ApplicationStateManager applicationStateManager,Map<String,BreadCrumbsFilter> pathFilters,ComponentSource componentSource) {
+	public BreadCrumbsServiceImpl(ApplicationStateManager applicationStateManager,Collection<BreadCrumbsFilter> pathFilters,ComponentSource componentSource) {
 		this.applicationStateManager = applicationStateManager;
 		this.patternMatcher = createPathMatcher();
 		this.pathFilters = pathFilters;
 	}
 
 	public void addToBreadCrumbs(PageRenderRequestParameters params) {
+		
+		if(params.isLoopback()) {
+			throw new RuntimeException("IS loopback");
+		}
 		BreadCrumbsListImpl breadCrumbsList = (BreadCrumbsListImpl) applicationStateManager.get(BreadCrumbsList.class);
 		String pageName = params.getLogicalPageName();
-    	Component page = componentSource.getPage(pageName);
-    	boolean titleReset = false;
-    	
-    	for(String path:pathFilters.keySet()) {
-			if (patternMatcher.matches(path, pageName)) {	
-				BreadCrumbsFilter filter = pathFilters.get(path);
-				if(filter == BreadCrumbsFilter.IGNORE) {
-					return;
+		boolean ignorePage = false;
+		boolean resetCrumbs = false;
+		boolean dynamicTitle = false;
+		Iterator<BreadCrumbsFilter> iter = pathFilters.iterator();
+		while(iter.hasNext()) {
+			BreadCrumbsFilter filter = iter.next();
+			if (patternMatcher.matches(filter.getPattern(), pageName)) {
+				BreadCrumbsFilterType type = filter.getFilterType();
+				if(type == BreadCrumbsFilterType.IGNORE) {
+					ignorePage = true;
 				}
 				
-				if(filter == BreadCrumbsFilter.TITLE_CHANGE) {
-					titleReset = true;
+				if(type == BreadCrumbsFilterType.RESET) {
+					resetCrumbs = true;
 				}
 				
-				if(filter == BreadCrumbsFilter.RESET) {
-					breadCrumbsList.clear();
-					break;
+				if(type == BreadCrumbsFilterType.DYNAMIC_TITLE) {
+					dynamicTitle = true;
 				}
 			}
 		}
-
+    	Component page = componentSource.getPage(pageName);
     	BreadCrumb breadCrumb = findAnnotation(page.getClass(),BreadCrumb.class);
     	if(breadCrumb != null) {
+	    	if(breadCrumb.ignore()) {
+	    		ignorePage = true;
+	    	}
+	    	
+	    	
 	    	if(breadCrumb.reset()) {
-				breadCrumbsList.clear();
+	    		resetCrumbs = true;
 	    	}
 	    	
-	    	if(breadCrumb.ignore()) {
-	    		return;
+	    	if(breadCrumb.dynamicTitle()) {
+	    		dynamicTitle = true;
 	    	}
+    	}    
+    	
+    	if(resetCrumbs) {
+    		breadCrumbsList.clear();
     	}
     	
- 
-    	String title = null;
-    	if(breadCrumb == null || !breadCrumb.titleReset() || !titleReset) {
-    		title = BreadCrumbUtils.getTitle(page, params);
+    	if(ignorePage) {
+    		return;
     	}
-    
-		breadCrumbsList.add(new BreadCrumbObject(params,title));
+		breadCrumbsList.add(new BreadCrumbObject(pageName,params.getActivationContext(),dynamicTitle));
 	}
 	
 	

src/main/java/com/zenios/tapestry/breadcrumbs/session/BreadCrumbsListImpl.java

-package com.zenios.tapestry.breadcrumbs.session;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.zenios.tapestry.breadcrumbs.other.BreadCrumbObject;
-
-public class BreadCrumbsListImpl implements BreadCrumbsList {
-	private static final long serialVersionUID = -8457209111821626779L;
-	private final List<BreadCrumbObject> items;
-	private final Object mutex;
-	private final int size;
-	private final boolean discardDuplicates;
-
-	public BreadCrumbsListImpl(int size,boolean discardDuplicates) {
-		this.items = new ArrayList<BreadCrumbObject>(size);
-		this.mutex = new Object();
-		this.size = size;
-		this.discardDuplicates = discardDuplicates;
-	}
-
-	public void clear() {
-		synchronized(mutex) {
-			items.clear();
-		}
-	}
-
-	public void add(BreadCrumbObject breadCrumb) {
-		synchronized(mutex) {
-			final int index = items.indexOf(breadCrumb);
-			if (discardDuplicates && index != -1) {
-				items.subList(index + 1, items.size()).clear();
-			} else {
-				while (items.size() == size) {
-					items.remove(0);
-				}
-				items.add(breadCrumb);
-			}
-		}
-	}
-
-	public List<BreadCrumbObject> getBreadCrumbs() {
-		synchronized(mutex) {
-			return new ArrayList<BreadCrumbObject>(items);
-		}
-	}
-}

src/main/java/com/zenios/tapestry/breadcrumbs/session/impl/BreadCrumbsListImpl.java

+package com.zenios.tapestry.breadcrumbs.session.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.zenios.tapestry.breadcrumbs.other.BreadCrumbObject;
+import com.zenios.tapestry.breadcrumbs.session.BreadCrumbsList;
+
+public class BreadCrumbsListImpl implements BreadCrumbsList {
+	private static final long serialVersionUID = -8457209111821626779L;
+	private final List<BreadCrumbObject> items;
+	private final Object mutex;
+	private final int size;
+	private final boolean discardDuplicates;
+
+	public BreadCrumbsListImpl(int size,boolean discardDuplicates) {
+		this.items = new ArrayList<BreadCrumbObject>(size);
+		this.mutex = new Object();
+		this.size = size;
+		this.discardDuplicates = discardDuplicates;
+	}
+
+	public void clear() {
+		synchronized(mutex) {
+			items.clear();
+		}
+	}
+
+	public void add(BreadCrumbObject breadCrumb) {
+		synchronized(mutex) {
+			final int index = items.lastIndexOf(breadCrumb);
+			/*No need to add the page since is exactly the same
+			 * as the previous one */
+			if(index != -1 && index == items.size() -1) {
+				return;
+			}
+			if (discardDuplicates) {;
+				if(index != -1) {
+					items.subList(index + 1, items.size()).clear();
+					return;
+				}
+			} else {
+				while (items.size() == size) {
+					items.remove(0);
+				}
+			}
+			items.add(breadCrumb);
+		}
+	}
+
+	public List<BreadCrumbObject> getBreadCrumbs() {
+		synchronized(mutex) {
+			return new ArrayList<BreadCrumbObject>(items);
+		}
+	}
+}

src/main/java/com/zenios/tapestry/breadcrumbs/utils/BreadCrumbUtils.java

-package com.zenios.tapestry.breadcrumbs.utils;
-
-import org.apache.tapestry5.ComponentEventCallback;
-import org.apache.tapestry5.internal.util.Holder;
-import org.apache.tapestry5.ioc.Messages;
-import org.apache.tapestry5.runtime.Component;
-import org.apache.tapestry5.services.PageRenderRequestParameters;
-
-import com.zenios.tapestry.breadcrumbs.other.BreadCrumbsEvents;
-
-public class BreadCrumbUtils {
-	public static String getTitle(Component page,PageRenderRequestParameters params) {
-		String pageName = params.getLogicalPageName();
-		String title = null;
-		final Holder<String> valueHolder = Holder.create();
-		ComponentEventCallback<String> callback = new ComponentEventCallback<String>()
-				{
-			public boolean handleResult(String result)
-			{
-				valueHolder.put(result);
-
-				return true;
-			}
-				};
-
-
-			page.getComponentResources().triggerContextEvent(BreadCrumbsEvents.GET_TITLE, params.getActivationContext(), callback);
-			title = valueHolder.get();
-			if(title == null) {
-				String key = new StringBuilder(pageName).append("-crumb").toString();
-				Messages messages = page.getComponentResources().getMessages();
-				if(messages.contains(key)) {
-					title = messages.get(key);
-				}
-			}
-			title = title != null ? title : pageName;
-			return title;
-	}
-}

src/main/resources/com/zenios/tapestry/breadcrumbs/Assets/default.css

+DIV.t-zbreadcrumbs UL {
+ 	margin: 0;
+  	padding: 0;
+	list-style: none;
+}
+
+DIV.t-zbreadcrumbs UL LI {
+	float:left;
+}
+
+ul.t-zbreadcrumbs {
+  overflow: hidden;
+  width: 100%;
+}
+
+ul.t-zbreadcrumbs li{
+  float: left;
+  margin: 0 .5em 0 1em;
+}
+
+ul.t-zbreadcrumbs a{
+  background: #ddd;
+  padding: .7em 1em;
+  float: left;
+  text-decoration: none;
+  color: #444;
+  text-shadow: 0 1px 0 rgba(255,255,255,.5);
+  position: relative;
+}
+
+ul.t-zbreadcrumbs a:hover{
+  background: #99db76;
+}
+
+ul.t-zbreadcrumbs a::before {
+  content: "";
+  position: absolute;
+  top: 50%;
+  margin-top: -1.5em;
+  border-width: 1.5em 0 1.5em 1em;
+  border-style: solid;
+  border-color: #ddd #ddd #ddd transparent;
+  left: -1em;
+}
+
+ul.t-zbreadcrumbs a:hover::before{
+  border-color: #99db76 #99db76 #99db76 transparent;
+}
+
+ul.t-zbreadcrumbs a::after{
+  content: "";
+  position: absolute;
+  top: 50%;
+  margin-top: -1.5em;
+  border-top: 1.5em solid transparent;
+  border-bottom: 1.5em solid transparent;
+  border-left: 1em solid #ddd;
+  right: -1em;
+}
+
+ul.t-zbreadcrumbs a:hover::after{
+  border-left-color: #99db76;
+}

src/main/resources/com/zenios/tapestry/breadcrumbs/components/BreadCrumbInfo.tml

-<li xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
-	${title}
-</li>
+<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
+    <t:block id="before" />
+	<t:delegate to="title"/>
+	<t:block id="after" />
+	
+	<t:block id="title">
+    	<t:pageLink t:page="${page}" t:context="activationContext">
+			${nameTitle}
+		</t:pageLink>
+	</t:block>
+</t:container>

src/main/resources/com/zenios/tapestry/breadcrumbs/components/BreadCrumbsLoop.tml

-<li rowClass="${rowClass}" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
+<li class="${rowClass}" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
 	<div t:id="breadCrumb"/>
 </li>

src/main/resources/com/zenios/tapestry/breadcrumbs/components/BreadCrumbsTrail.tml

 <div class="t-zbreadcrumbs" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
-
     <ul t:id="trail">
         <tbody>
         	<li t:id="trailCrumbs"/>
         </tbody>
     </ul>
-
-    <t:block id="empty">${message:no-grid-data-to-display}</t:block>
-
 </div>