Author: rgoers Date: Sun Nov 7 14:26:17 2004 New Revision: 56872 Added: cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/event/aspect/impl/PageLabelEventAspect.java cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/event/impl/PageLabelEventConverter.java cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/impl/PageLabelLinkService.java cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/impl/PageLabelManager.java Log: bug 31857 - Add Portal PageLabels
Added: cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/event/aspect/impl/PageLabelEventAspect.java ============================================================================== --- (empty file) +++ cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/event/aspect/impl/PageLabelEventAspect.java Sun Nov 7 14:26:17 2004 @@ -0,0 +1,97 @@ +/* + * Copyright 1999-2002,2004 The Apache Software Foundation. + * + * Licensed 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.cocoon.portal.event.aspect.impl; + +import java.util.Iterator; + +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.thread.ThreadSafe; +import org.apache.avalon.framework.activity.Disposable; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.portal.PortalService; +import org.apache.cocoon.portal.impl.PageLabelManager; +import org.apache.cocoon.portal.event.Event; +import org.apache.cocoon.portal.event.Publisher; +import org.apache.cocoon.portal.event.aspect.EventAspect; +import org.apache.cocoon.portal.event.aspect.EventAspectContext; + +/** + * Converts links generated by the PageLabelLinkService into events and publishes them. + * Used in conjunction with the PageLabelLinkService, links generated from the layout + * portal.xml will be based upon the names of the named items. + * + * @author Ralph Goers + * + * @version CVS $Id: $ + */ +public class PageLabelEventAspect extends AbstractLogEnabled + implements EventAspect, ThreadSafe, Serviceable, Disposable { + + protected ServiceManager manager; + + protected PageLabelManager labelManager; + + public void service(ServiceManager manager) throws ServiceException + { + this.manager = manager; + this.labelManager = (PageLabelManager)manager.lookup(PageLabelManager.ROLE); + } + + public void dispose() + { + if (this.manager != null) { + if (this.labelManager != null) { + this.manager.release(this.labelManager); + this.labelManager = null; + } + this.manager = null; + } + } + + /* (non-Javadoc) + * @see org.apache.cocoon.portal.event.aspect.EventAspect#process(org.apache.cocoon.portal.event.aspect.EventAspectContext, org.apache.cocoon.portal.PortalService) + */ + public void process(EventAspectContext context, PortalService service) { + if (this.labelManager != null) { + final Publisher publisher = context.getEventPublisher(); + final Request request = ObjectModelHelper.getRequest(context.getObjectModel()); + final String parameterName = this.labelManager.getRequestParameterName(); + + String label = request.getParameter(parameterName); + // The pageLabel must be single valued + if (label != null) { + String previous = this.labelManager.getPreviousLabel(); + if (previous != null && previous.equals(label)) { + // Already on this page. Don't publish the pageLabel events + } else { + Iterator iter = this.labelManager.getPageLabelEvents(label).iterator(); + // Publish all the events for this page label. + while (iter.hasNext()) { + Event event = (Event) iter.next(); + publisher.publish(event); + } + // return; + } + } + } + + context.invokeNext( service ); + } +} \ No newline at end of file Added: cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/event/impl/PageLabelEventConverter.java ============================================================================== --- (empty file) +++ cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/event/impl/PageLabelEventConverter.java Sun Nov 7 14:26:17 2004 @@ -0,0 +1,123 @@ +/* + * Copyright 1999-2002,2004 The Apache Software Foundation. + * + * Licensed 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.cocoon.portal.event.impl; + +import java.util.List; +import java.util.Map; +import java.util.ArrayList; + +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.thread.ThreadSafe; +import org.apache.cocoon.portal.impl.PageLabelManager; +import org.apache.cocoon.portal.event.Event; +import org.apache.cocoon.portal.event.EventConverter; + +/** + * Convert events from and into strings. + * @author Ralph Goers + * + * @version CVS $Id: $ + */ +public class PageLabelEventConverter extends AbstractLogEnabled + implements EventConverter, Serviceable, ThreadSafe { + + protected PageLabelManager labelManager; + + private static final String ENCODE = "&ENCODE"; + private static final String DECODE = "&DECODE"; + + protected ServiceManager manager; + + /* (non-Javadoc) + * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) + */ + public void service(ServiceManager manager) throws ServiceException { + this.manager = manager; + this.labelManager = (PageLabelManager)manager.lookup(PageLabelManager.ROLE); + } + + /* (non-Javadoc) + * @see org.apache.cocoon.portal.event.EventConverter#encode(org.apache.cocoon.portal.event.Event) + */ + public String encode(Event event) { + + String pageLabel = this.labelManager.getCurrentLabel(); + if (pageLabel == null) { + pageLabel = ""; + } + Map map = this.labelManager.getPageEventMap(); + String encode = pageLabel + ENCODE; + List list = (List)map.get(encode); + + if (null == list) { + list = new ArrayList(); + map.put(encode, list); + } + + int index = list.indexOf(event); + if ( index == -1 ) { + list.add(event); + index = list.size() - 1; + } + return String.valueOf(index); + } + + /* (non-Javadoc) + * @see org.apache.cocoon.portal.event.EventConverter#decode(java.lang.String) + */ + public Event decode(String value) { + String pageLabel = this.labelManager.getCurrentLabel(); + if (pageLabel == null) { + pageLabel = ""; + } + Map map = this.labelManager.getPageEventMap(); + List list = (List)map.get(pageLabel + DECODE); + + if ( null != list ) { + int index = new Integer(value).intValue(); + if (index < list.size()) { + return (Event)list.get(index); + } + } + return null; + } + + /* (non-Javadoc) + * @see org.apache.cocoon.portal.event.EventConverter#start() + */ + public void start() { + String label = this.labelManager.setCurrentLabel(); + Map map = this.labelManager.getPageEventMap(); + String encode = label + ENCODE; + String decode = label + DECODE; + + List list = (List)map.get(encode); + + if (null != list) { + map.put(decode, list); + map.remove(encode); + } + } + + /* (non-Javadoc) + * @see org.apache.cocoon.portal.event.EventConverter#finish() + */ + public void finish() { + } +} \ No newline at end of file Added: cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/impl/PageLabelLinkService.java ============================================================================== --- (empty file) +++ cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/impl/PageLabelLinkService.java Sun Nov 7 14:26:17 2004 @@ -0,0 +1,230 @@ +/* + * Copyright 1999-2002,2004 The Apache Software Foundation. + * + * Licensed 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.cocoon.portal.impl; + +import java.util.Iterator; +import java.util.List; + +import org.apache.cocoon.portal.event.Event; +import org.apache.cocoon.portal.event.impl.ChangeAspectDataEvent; +import org.apache.cocoon.portal.layout.CompositeLayout; +import org.apache.cocoon.portal.layout.Item; +import org.apache.cocoon.portal.layout.NamedItem; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.excalibur.source.SourceUtil; + +/** + * The PageLabelLinkService generates links for named items defined in the layout portal.xml. + * Links for other entities are passed to the DefaultLinkService to be resolved. + * + * @author Ralph Goers + * + * @version CVS $Id: $ + */ +public class PageLabelLinkService extends DefaultLinkService { + + protected String apectName; + + /** The label manager */ + protected PageLabelManager labelManager; + + /* (non-Javadoc) + * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) + */ + public void service(ServiceManager manager) throws ServiceException { + super.service(manager); + this.labelManager = (PageLabelManager)this.manager.lookup(PageLabelManager.ROLE); + } + + /** + * Get the uri for the coplet containing event + * + * @param event The event to find + * @return A URI + */ + public String getLinkURI(Event event) { + if (event == null) { + return this.getRefreshLinkURI(); + } + if (this.labelManager == null) { + super.getLinkURI(event); + } + + String requestParameterName = this.labelManager.getRequestParameterName(); + + if (event instanceof ChangeAspectDataEvent && + ((ChangeAspectDataEvent) event).getTarget() instanceof CompositeLayout) { + + ChangeAspectDataEvent e = (ChangeAspectDataEvent)event; + CompositeLayout layout = (CompositeLayout)e.getTarget(); + int i = ((Integer)e.getData()).intValue(); + + Item item = layout.getItem(i); + if (item instanceof NamedItem) { + StringBuffer key = new StringBuffer(""); + getKey(item, key); + + if (this.labelManager.getPageLabelEvents(key.toString()) != null) { + final Info info = this.getInfo(); + boolean hasParams = info.hasParameters; + final StringBuffer buffer = new StringBuffer(info.linkBase.toString()); + if (hasParams) { + buffer.append('&'); + } + else { + buffer.append('?'); + } + buffer.append(requestParameterName).append('=') + .append(SourceUtil.encode(key.toString())); + return buffer.toString(); + } + } + } + + String label = this.labelManager.getCurrentLabel(); + + return getLink(super.getLinkURI(event), requestParameterName, label); + } + + /** + * Get the uri for this coplet containing the additional events. + * + * @param events The events that will be processed by the generated uri. + * @return A URI + */ + public String getLinkURI(List events) + { + if (events == null || events.size() == 0) { + return this.getRefreshLinkURI(); + } + if (this.labelManager == null) { + super.getLinkURI(events); + } + + String requestParameterName = this.labelManager.getRequestParameterName(); + final Info info = this.getInfo(); + final StringBuffer buffer = new StringBuffer(info.linkBase.toString()); + boolean hasParams = info.hasParameters; + Iterator iter = events.iterator(); + StringBuffer value = new StringBuffer(""); + + while (iter.hasNext()) + { + Event event = (Event)iter.next(); + + if (event instanceof ChangeAspectDataEvent && + ((ChangeAspectDataEvent) event).getTarget() instanceof CompositeLayout) { + + ChangeAspectDataEvent e = (ChangeAspectDataEvent) event; + CompositeLayout layout = (CompositeLayout) e.getTarget(); + int i = ((Integer) e.getData()).intValue(); + + Item item = layout.getItem(i); + if (value.length() > 0) { + value.append('.'); + } + if (item instanceof NamedItem) { + value.append(((NamedItem)item).getName()); + } + else { + value.append(Integer.toString(i)); + } + } + else { + String label = this.labelManager.getCurrentLabel(); + + return getLink(super.getLinkURI(events), requestParameterName, label); + } + } + + if (value.length() > 0 && this.labelManager.getPageLabelEvents(value.toString()) != null) { + if (hasParams) { + buffer.append('&'); + } + else { + buffer.append('?'); + } + buffer.append(requestParameterName).append('=') + .append(SourceUtil.encode(value.toString())); + + return buffer.toString(); + } + + String label = this.labelManager.getCurrentLabel(); + + return getLink(super.getLinkURI(events), requestParameterName, label); + } + + /* (non-Javadoc) + * @see org.apache.avalon.framework.activity.Disposable#dispose() + */ + public void dispose() { + if (this.manager != null) { + if (this.labelManager != null) { + this.manager.release(this.labelManager); + this.labelManager = null; + } + } + super.dispose(); + } + + /* + * Generates the page label. + * @param item An Item. + * @param key The StringBuffer in which to create the page label. + */ + private void getKey(Item item, StringBuffer key) { + CompositeLayout parentLayout = item.getParent(); + Item parentItem = parentLayout.getParent(); + + if (parentItem != null) { + getKey(parentItem, key); + } + + if (key.length() > 0) { + key.append('.'); + } + if (item instanceof NamedItem) { + key.append(((NamedItem) item).getName()); + } + else { + key.append(parentLayout.getItems().indexOf(item)); + } + } + + /* + * Append the page label to the link. + * @param link The link to add the label to. + * @param parmName The request parameter name. + * @param label The page label. + * @return The modified link. + */ + private String getLink(String link, String parmName, String label) + { + if (label == null) { + return link; + } + StringBuffer uri = new StringBuffer(link); + if (link.indexOf('?') >= 0) { + uri.append('&'); + } else { + uri.append('?'); + } + uri.append(parmName).append('=').append(SourceUtil.encode(label)); + return uri.toString(); + } +} \ No newline at end of file Added: cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/impl/PageLabelManager.java ============================================================================== --- (empty file) +++ cocoon/branches/BRANCH_2_1_X/src/blocks/portal/java/org/apache/cocoon/portal/impl/PageLabelManager.java Sun Nov 7 14:26:17 2004 @@ -0,0 +1,305 @@ +/* + * Copyright 1999-2002,2004 The Apache Software Foundation. + * + * Licensed 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.cocoon.portal.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.avalon.framework.context.Context; +import org.apache.avalon.framework.context.ContextException; +import org.apache.avalon.framework.context.Contextualizable; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.avalon.framework.thread.ThreadSafe; +import org.apache.avalon.framework.CascadingRuntimeException; +import org.apache.avalon.framework.component.Component; +import org.apache.avalon.framework.configuration.Configurable; +import org.apache.avalon.framework.configuration.Configuration; +import org.apache.cocoon.portal.PortalService; +import org.apache.cocoon.portal.layout.Layout; +import org.apache.cocoon.portal.layout.CompositeLayout; +import org.apache.cocoon.portal.layout.Item; +import org.apache.cocoon.portal.layout.NamedItem; +import org.apache.cocoon.portal.event.impl.ChangeAspectDataEvent; +import org.apache.cocoon.util.HashMap; +import org.apache.cocoon.environment.Request; +import org.apache.cocoon.environment.ObjectModelHelper; +import org.apache.cocoon.components.ContextHelper; + +/** + * Manages the various activities required for page labels. + * + * The name of the request parameter used to identify the page labelmay be configured + * here by declaring + * <br><parameter-name><i>request-parm-name</i></parameter-name><br> + * in the configuration for this component. The default request parameter name is + * 'pageLabel'. + * @author Ralph Goers + * + * @version CVS $Id: $ + */ +public class PageLabelManager + extends AbstractLogEnabled + implements ThreadSafe, Serviceable, Configurable, Contextualizable, Component { + + public static final String ROLE = PageLabelManager.class.getName(); + + /** The service manager */ + protected ServiceManager manager; + /** The cocoon context */ + protected Context context; + protected String aspectName = null; + private String requestParameterName; + private boolean nonStickyTabs; + + protected static final String LABEL_ARRAY = PageLabelManager.class.getName() + "A"; + protected static final String LABEL_MAP = PageLabelManager.class.getName() + "M"; + protected static final String EVENT_MAP = PageLabelManager.class.getName() + "E"; + private static final String DEFAULT_REQUEST_PARAMETER_NAME = "pageLabel"; + + + /* (non-Javadoc) + * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) + */ + public void service(ServiceManager manager) throws ServiceException { + this.manager = manager; + } + + /* (non-Javadoc) + * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) + */ + public void configure(Configuration config) + { + this.requestParameterName = + config.getChild("parameterName").getValue(DEFAULT_REQUEST_PARAMETER_NAME); + this.aspectName = config.getChild("aspectName").getValue("tab"); + this.nonStickyTabs = + Boolean.valueOf(config.getChild("nonStickyTabs").getValue("false")).booleanValue(); + } + + /* (non-Javadoc) + * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) + */ + public void contextualize(Context context) throws ContextException { + this.context = context; + } + + /** + * Return the current page label. + * @return The current page label. + */ + public String getCurrentLabel() { + String[] labels = getLabels(); + + return labels[0]; + } + + /** + * Return the page label from the previous request. + * @return The previous page label. + */ + public String getPreviousLabel() { + String[] labels = getLabels(); + + return labels[1]; + } + + /** + * Sets the current page label. + * @return The current page label. + */ + public String setCurrentLabel() { + final Request request = + ObjectModelHelper.getRequest(ContextHelper.getObjectModel(this.context)); + String value = request.getParameter(this.requestParameterName); + String[] labels = getLabels(); + + if (value != null) { + labels[1] = labels[0]; + labels[0] = value; + } + return labels[0]; + } + + /** + * Returns the request parameter being used to identify the page label. + * @return A String containing the request parameter name used for page labels. + */ + public String getRequestParameterName() { + return this.requestParameterName; + } + + + /** + * Return the Map that contains events for all the page labels. + * @return The Map to use for converting events to and from urls. + */ + public Map getPageEventMap() + { + PortalService service = null; + try { + service = (PortalService) this.manager.lookup(PortalService.ROLE); + Map map = (Map) service.getAttribute(EVENT_MAP); + if (null == map) { + map = new HashMap(); + service.setAttribute(EVENT_MAP, map); + } + + return map; + } + catch (ServiceException ce) { + throw new CascadingRuntimeException("Unable to lookup component.", ce); + } + finally { + this.manager.release(service); + } + } + + /** + * Retrieve the events associated with the specified page label. + * + * @param pageLabel The label to retrieve the events for. + * @return A List containing all the events associated with the page label in the order they + * should occur. + */ + public List getPageLabelEvents(String pageLabel) { + PortalService service = null; + try { + service = (PortalService) this.manager.lookup(PortalService.ROLE); + Map map = (Map) service.getAttribute(LABEL_MAP); + if (null == map) { + map = initializeLabels(service); + service.setAttribute(LABEL_MAP, map); + } + + List list = (List) map.get(pageLabel); + + if (list == null) { + list = new ArrayList(); + map.put(pageLabel, list); + } + + return list; + } + catch (ServiceException ce) { + throw new CascadingRuntimeException("Unable to lookup component.", ce); + } + finally { + this.manager.release(service); + } + } + + /* + * Return the array containing the current and previous labels. + */ + private String[] getLabels() { + PortalService service = null; + try { + service = (PortalService) this.manager.lookup(PortalService.ROLE); + String[] labels = (String[]) service.getAttribute(LABEL_ARRAY); + if (null == labels) { + labels = new String[2]; + service.setAttribute(LABEL_ARRAY, labels); + } + return labels; + } + catch (ServiceException ce) { + throw new CascadingRuntimeException("Unable to lookup component.", ce); + } + finally { + this.manager.release(service); + } + } + + /** + * Create the page label event map and return it. + * + * @param service The portal service + * @return The page label map. + */ + private Map initializeLabels(PortalService service) { + Map map = new HashMap(); + + Layout portalLayout = service.getEntryLayout(null); + if (portalLayout == null) { + portalLayout = + service.getComponentManager().getProfileManager().getPortalLayout(null, null); + } + + if (portalLayout instanceof CompositeLayout) { + populate((CompositeLayout) portalLayout, map, "", new ArrayList()); + } + + return map; + } + + + /** + * Populate the event map + * + * @param layout + * @param map + * @param name + * @param parentEvents + */ + private List populate(CompositeLayout layout, Map map, String name, List parentEvents) { + List lhList = null; + for (int j = 0; j < layout.getSize(); j++) { + Item tab = layout.getItem(j); + ChangeAspectDataEvent event = + new ChangeAspectDataEvent(layout, this.aspectName, new Integer(j)); + StringBuffer label = new StringBuffer(name); + if (label.length() > 0) { + label.append("."); + } + label.append((tab instanceof NamedItem) ? ((NamedItem) tab).getName() : + Integer.toString(j)); + List events = new ArrayList(parentEvents); + events.add(event); + Layout child = tab.getLayout(); + List allEvents = null; + if (child != null && child instanceof CompositeLayout) { + allEvents = populate((CompositeLayout) child, map, label.toString(), events); + } + if (this.nonStickyTabs) { + // With non-sticky tabs the non-leaf nodes always display + // the left-most child tabs + if (lhList == null) + { + if (allEvents != null) + { + lhList = allEvents; + } + else + { + lhList = events; + } + } + if (allEvents != null) { + map.put(label.toString(), allEvents); + } else { + map.put(label.toString(), events); + } + } else { + map.put(label.toString(), events); + } + } + return lhList; + } +} \ No newline at end of file