001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.internal.services;
016:
017: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
018: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
019: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newThreadSafeList;
020: import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
021: import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
022:
023: import java.util.Collection;
024: import java.util.List;
025: import java.util.Map;
026:
027: import org.apache.tapestry.ComponentEventHandler;
028: import org.apache.tapestry.Link;
029: import org.apache.tapestry.TapestryConstants;
030: import org.apache.tapestry.internal.InternalConstants;
031: import org.apache.tapestry.internal.TapestryInternalUtils;
032: import org.apache.tapestry.internal.structure.ComponentPageElement;
033: import org.apache.tapestry.internal.structure.Page;
034: import org.apache.tapestry.ioc.services.TypeCoercer;
035: import org.apache.tapestry.ioc.util.StrategyRegistry;
036: import org.apache.tapestry.runtime.Component;
037: import org.apache.tapestry.services.ComponentClassResolver;
038: import org.apache.tapestry.services.Request;
039: import org.apache.tapestry.services.Response;
040:
041: public class LinkFactoryImpl implements LinkFactory {
042: private final Request _request;
043:
044: private final Response _response;
045:
046: private final ComponentClassResolver _componentClassResolver;
047:
048: private final ComponentInvocationMap _componentInvocationMap;
049:
050: private final RequestPageCache _pageCache;
051:
052: private final TypeCoercer _typeCoercer;
053:
054: private final List<LinkFactoryListener> _listeners = newThreadSafeList();
055:
056: private final StrategyRegistry<PassivateContextHandler> _registry;
057:
058: private interface PassivateContextHandler<T> {
059: void handle(T result, List context);
060: }
061:
062: public LinkFactoryImpl(Request request, Response encoder,
063: ComponentClassResolver componentClassResolver,
064: ComponentInvocationMap componentInvocationMap,
065: RequestPageCache pageCache, TypeCoercer typeCoercer) {
066: _request = request;
067: _response = encoder;
068: _componentClassResolver = componentClassResolver;
069: _componentInvocationMap = componentInvocationMap;
070: _pageCache = pageCache;
071: _typeCoercer = typeCoercer;
072:
073: Map<Class, PassivateContextHandler> registrations = newMap();
074:
075: registrations.put(Object.class, new PassivateContextHandler() {
076: @SuppressWarnings("unchecked")
077: public void handle(Object result, List context) {
078: context.add(result);
079: }
080: });
081:
082: registrations.put(Object[].class,
083: new PassivateContextHandler<Object[]>() {
084:
085: @SuppressWarnings("unchecked")
086: public void handle(Object[] result, List context) {
087: for (Object o : result)
088: context.add(o);
089: }
090: });
091:
092: registrations.put(Collection.class,
093: new PassivateContextHandler<Collection>() {
094: @SuppressWarnings("unchecked")
095: public void handle(Collection result, List context) {
096: context.addAll(result);
097: }
098: });
099:
100: _registry = StrategyRegistry.newInstance(
101: PassivateContextHandler.class, registrations);
102: }
103:
104: public void addListener(LinkFactoryListener listener) {
105: _listeners.add(listener);
106: }
107:
108: public Link createActionLink(ComponentPageElement component,
109: String action, boolean forForm, Object... context) {
110: notBlank(action, "action");
111:
112: Page containingPage = component.getContainingPage();
113:
114: String logicalPageName = containingPage.getLogicalName();
115:
116: ActionLinkTarget target = new ActionLinkTarget(action,
117: logicalPageName, component.getNestedId());
118:
119: String[] contextStrings = toContextStrings(context);
120:
121: String[] activationContext = collectActivationContextForPage(containingPage);
122:
123: ComponentInvocation invocation = new ComponentInvocation(
124: target, contextStrings, activationContext);
125:
126: Link link = new LinkImpl(_response, _request.getContextPath(),
127: invocation, forForm);
128:
129: // Now see if the page has an activation context.
130:
131: addActivationContextToLink(link, activationContext);
132:
133: // TODO: query parameter for case where active page != component page.
134:
135: _componentInvocationMap.store(link, invocation);
136:
137: for (LinkFactoryListener listener : _listeners)
138: listener.createdActionLink(link);
139:
140: return link;
141: }
142:
143: private void addActivationContextToLink(Link link,
144: String[] activationContext) {
145: if (activationContext.length == 0)
146: return;
147:
148: StringBuilder builder = new StringBuilder();
149:
150: for (int i = 0; i < activationContext.length; i++) {
151: if (i > 0)
152: builder.append("/");
153:
154: builder.append(TapestryInternalUtils
155: .urlEncode(activationContext[i]));
156: }
157:
158: link.addParameter(InternalConstants.PAGE_CONTEXT_NAME, builder
159: .toString());
160:
161: }
162:
163: public Link createPageLink(final Page page, boolean override,
164: Object... activationContext) {
165: notNull(page, "page");
166:
167: String logicalPageName = page.getLogicalName();
168:
169: // When override is true, we use the activation context even if empty.
170:
171: String[] context = (override || activationContext.length != 0) ? toContextStrings(activationContext)
172: : collectActivationContextForPage(page);
173:
174: PageLinkTarget target = new PageLinkTarget(logicalPageName);
175: ComponentInvocation invocation = new ComponentInvocation(
176: target, context, null);
177:
178: Link link = new LinkImpl(_response, _request.getContextPath(),
179: invocation, false);
180:
181: _componentInvocationMap.store(link, invocation);
182:
183: for (LinkFactoryListener listener : _listeners)
184: listener.createdPageLink(link);
185:
186: return link;
187: }
188:
189: /**
190: * Returns a list of objects acquired by invoking triggering the passivate event on the page's
191: * root element. May return an empty list.
192: */
193: private String[] collectActivationContextForPage(final Page page) {
194: final List context = newList();
195:
196: ComponentEventHandler handler = new ComponentEventHandler() {
197: @SuppressWarnings("unchecked")
198: public boolean handleResult(Object result,
199: Component component, String methodDescription) {
200: PassivateContextHandler contextHandler = _registry
201: .getByInstance(result);
202:
203: contextHandler.handle(result, context);
204:
205: return true;
206: }
207: };
208:
209: ComponentPageElement rootElement = page.getRootElement();
210:
211: rootElement.triggerEvent(TapestryConstants.PASSIVATE_EVENT,
212: null, handler);
213:
214: return toContextStrings(context.toArray());
215: }
216:
217: private String[] toContextStrings(Object[] context) {
218: if (context == null)
219: return new String[0];
220:
221: String[] result = new String[context.length];
222:
223: for (int i = 0; i < context.length; i++)
224: result[i] = _typeCoercer.coerce(context[i], String.class);
225:
226: return result;
227: }
228:
229: public Link createPageLink(String pageName, boolean override,
230: Object... context) {
231: // This verifies that the page name is valid.
232: Page page = _pageCache.get(pageName);
233:
234: return createPageLink(page, override, context);
235: }
236: }
|