001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.request.target.coding;
018:
019: import java.lang.ref.WeakReference;
020:
021: import org.apache.wicket.Application;
022: import org.apache.wicket.IRedirectListener;
023: import org.apache.wicket.IRequestTarget;
024: import org.apache.wicket.MetaDataKey;
025: import org.apache.wicket.Page;
026: import org.apache.wicket.PageParameters;
027: import org.apache.wicket.RequestCycle;
028: import org.apache.wicket.Session;
029: import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy;
030: import org.apache.wicket.request.RequestParameters;
031: import org.apache.wicket.request.target.component.BookmarkableListenerInterfaceRequestTarget;
032: import org.apache.wicket.request.target.component.BookmarkablePageRequestTarget;
033: import org.apache.wicket.request.target.component.PageRequestTarget;
034: import org.apache.wicket.request.target.component.listener.ListenerInterfaceRequestTarget;
035: import org.apache.wicket.util.string.AppendingStringBuffer;
036: import org.apache.wicket.util.string.Strings;
037:
038: /**
039: * An URL coding strategy that encodes the mount point, page parameters and page
040: * instance information into the URL. The benefits compared to mounting page
041: * with {@link BookmarkablePageRequestTargetUrlCodingStrategy} are that the
042: * mount point is preserved even after invoking listener interfaces (thus you
043: * don't lose bookmarkability after clicking links) and that for ajax only pages
044: * the state is preserved on refresh.
045: * <p>
046: * The url with {@link HybridUrlCodingStrategy} looks like
047: * /mount/path/param1/value1.3. or /mount/path/param1/value1.3.2 where 3 is page
048: * Id and 2 is version number.
049: * <p>
050: * Also to preserve state on refresh with ajax-only pages the
051: * {@link HybridUrlCodingStrategy} does an immediate redirect after hitting
052: * bookmarkable URL, e.g. it immediately redirects from /mount/path to
053: * /mount/path.3 where 3 is the next page id. This preserves the page instance
054: * on subsequent page refresh.
055: *
056: * @author Matej Knopp
057: */
058: public class HybridUrlCodingStrategy extends
059: AbstractRequestTargetUrlCodingStrategy {
060: /** bookmarkable page class. */
061: protected final WeakReference/* <Class> */pageClassRef;
062:
063: private final boolean redirectOnBookmarkableRequest;
064:
065: /**
066: * Construct.
067: *
068: * @param mountPath
069: * @param pageClass
070: * @param redirectOnBookmarkableRequest
071: * whether after hitting the page with URL in bookmarkable form
072: * it should be redirected to hybrid URL - needed for ajax to
073: * work properly after page refresh
074: */
075: public HybridUrlCodingStrategy(String mountPath, Class pageClass,
076: boolean redirectOnBookmarkableRequest) {
077: super (mountPath);
078: pageClassRef = new WeakReference(pageClass);
079: this .redirectOnBookmarkableRequest = redirectOnBookmarkableRequest;
080: }
081:
082: /**
083: * Construct.
084: *
085: * @param mountPath
086: * @param pageClass
087: */
088: public HybridUrlCodingStrategy(String mountPath, Class pageClass) {
089: this (mountPath, pageClass, true);
090: }
091:
092: /**
093: * Returns the amount of trailing slashes in the given string
094: *
095: * @param seq
096: * @return
097: */
098: private int getTrailingSlashesCount(CharSequence seq) {
099: int count = 0;
100: for (int i = seq.length() - 1; i >= 0; --i) {
101: if (seq.charAt(i) == '/') {
102: ++count;
103: } else {
104: break;
105: }
106: }
107: return count;
108: }
109:
110: /**
111: * Returns whether after hitting bookmarkable url the request should be
112: * redirected to a hybrid URL. This is recommended for pages with Ajax.
113: *
114: * @return
115: */
116: protected boolean isRedirectOnBookmarkableRequest() {
117: return redirectOnBookmarkableRequest;
118: }
119:
120: /**
121: * Returns whether to redirect when there is pageMap specified in
122: * bookmarkable URL
123: *
124: * @return
125: */
126: protected boolean alwaysRedirectWhenPageMapIsSpecified() {
127: // returns true if the pageId is unique, so we can get rid of the
128: // pageMap name in the url this way
129: return Application.exists()
130: && Application.get().getSessionSettings()
131: .isPageIdUniquePerSession();
132: }
133:
134: /**
135: * @see org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy#decode(org.apache.wicket.request.RequestParameters)
136: */
137: public IRequestTarget decode(RequestParameters requestParameters) {
138: String parametersFragment = requestParameters.getPath()
139: .substring(getMountPath().length());
140:
141: // try to extract page info
142: PageInfoExtraction extraction = extractPageInfo(parametersFragment);
143:
144: PageInfo pageInfo = extraction.getPageInfo();
145: String pageMapName = pageInfo != null ? pageInfo
146: .getPageMapName() : null;
147: Integer pageVersion = pageInfo != null ? pageInfo
148: .getVersionNumber() : null;
149: Integer pageId = pageInfo != null ? pageInfo.getPageId() : null;
150:
151: // decode parameters
152: PageParameters parameters = new PageParameters(
153: decodeParameters(extraction.getUrlAfterExtraction(),
154: requestParameters.getParameters()));
155:
156: if (requestParameters.getPageMapName() == null) {
157: requestParameters.setPageMapName(pageMapName);
158: } else {
159: pageMapName = requestParameters.getPageMapName();
160: }
161:
162: // do some extra work for checking whether this is a normal request to a
163: // bookmarkable page, or a request to a stateless page (in which case a
164: // wicket:interface parameter should be available
165: final String interfaceParameter = (String) parameters
166: .remove(WebRequestCodingStrategy.INTERFACE_PARAMETER_NAME);
167:
168: // we need to remember the amount of trailing slashes after the redirect
169: // (otherwise we'll break relative urls)
170: int originalUrlTrailingSlashesCount = getTrailingSlashesCount(extraction
171: .getUrlAfterExtraction());
172:
173: boolean redirect = isRedirectOnBookmarkableRequest();
174: if (Strings.isEmpty(pageMapName) != true
175: && alwaysRedirectWhenPageMapIsSpecified()) {
176: redirect = true;
177: }
178:
179: if (interfaceParameter != null) {
180: // stateless listener interface
181: WebRequestCodingStrategy.addInterfaceParameters(
182: interfaceParameter, requestParameters);
183: return new BookmarkableListenerInterfaceRequestTarget(
184: pageMapName, (Class) pageClassRef.get(),
185: parameters, requestParameters.getComponentPath(),
186: requestParameters.getInterfaceName());
187: } else if (pageId == null) {
188: // bookmarkable page request
189: return new HybridBookmarkablePageRequestTarget(pageMapName,
190: (Class) pageClassRef.get(), parameters,
191: originalUrlTrailingSlashesCount, redirect);
192: } else
193: // hybrid url
194: {
195: Page page;
196:
197: if (Strings.isEmpty(pageMapName)
198: && Application.exists()
199: && Application.get().getSessionSettings()
200: .isPageIdUniquePerSession()) {
201: page = Session.get().getPage(
202: pageId.intValue(),
203: pageVersion != null ? pageVersion.intValue()
204: : 0);
205: } else {
206: page = Session.get().getPage(
207: pageMapName,
208: "" + pageId,
209: pageVersion != null ? pageVersion.intValue()
210: : 0);
211: }
212:
213: // check if the found page match the required class
214: if (page != null
215: && page.getClass().equals(pageClassRef.get())) {
216: requestParameters
217: .setInterfaceName(IRedirectListener.INTERFACE
218: .getName());
219: RequestCycle.get().getRequest().setPage(page);
220: return new PageRequestTarget(page);
221: } else {
222: // we didn't find the page, act as bookmarkable page request -
223: // create new instance
224: return new HybridBookmarkablePageRequestTarget(
225: pageMapName, (Class) pageClassRef.get(),
226: parameters, originalUrlTrailingSlashesCount,
227: redirect);
228: }
229: }
230:
231: }
232:
233: /**
234: * Returns the number of traling slashes in the url when the page in request
235: * target was created or null if the number can't be determined.
236: *
237: * @param requestTarget
238: * @return
239: */
240: private Integer getOriginalOriginalTrailingSlashesCount(
241: IRequestTarget requestTarget) {
242: if (requestTarget instanceof ListenerInterfaceRequestTarget) {
243: ListenerInterfaceRequestTarget target = (ListenerInterfaceRequestTarget) requestTarget;
244: Page page = target.getPage();
245: return (Integer) page
246: .getMetaData(ORIGINAL_TRAILING_SLASHES_COUNT_METADATA_KEY);
247: }
248: return null;
249: }
250:
251: /**
252: * Extracts the PageParameters from given request target
253: *
254: * @param requestTarget
255: * @return
256: */
257: private PageParameters getPageParameters(
258: IRequestTarget requestTarget) {
259: if (requestTarget instanceof BookmarkablePageRequestTarget) {
260: BookmarkablePageRequestTarget target = (BookmarkablePageRequestTarget) requestTarget;
261: return target.getPageParameters();
262: } else if (requestTarget instanceof ListenerInterfaceRequestTarget) {
263: ListenerInterfaceRequestTarget target = (ListenerInterfaceRequestTarget) requestTarget;
264: Page page = target.getPage();
265: return getInitialPagePageParameters(page);
266: } else {
267: return null;
268: }
269: }
270:
271: /**
272: * Extracts the PageInfo from given request target
273: *
274: * @param requestTarget
275: * @return
276: */
277: private PageInfo getPageInfo(IRequestTarget requestTarget) {
278: if (requestTarget instanceof BookmarkablePageRequestTarget) {
279: BookmarkablePageRequestTarget target = (BookmarkablePageRequestTarget) requestTarget;
280: if (target.getPageMapName() != null) {
281: return new PageInfo(null, null, target.getPageMapName());
282: } else {
283: return null;
284: }
285: } else if (requestTarget instanceof ListenerInterfaceRequestTarget) {
286: ListenerInterfaceRequestTarget target = (ListenerInterfaceRequestTarget) requestTarget;
287: Page page = target.getPage();
288: return new PageInfo(new Integer(page.getNumericId()),
289: new Integer(page.getCurrentVersionNumber()), page
290: .getPageMapName());
291: } else {
292: return null;
293: }
294: }
295:
296: /**
297: * Sets the initial page parameters for page instance. Use this only if you
298: * know what you are doing.
299: *
300: * @param page
301: * @param pageParameters
302: */
303: public static void setInitialPageParameters(Page page,
304: PageParameters pageParameters) {
305: page.setMetaData(PAGE_PARAMETERS_META_DATA_KEY, pageParameters);
306: }
307:
308: /**
309: * @param page
310: * @return
311: */
312: public static PageParameters getInitialPagePageParameters(Page page) {
313: return (PageParameters) page
314: .getMetaData(PAGE_PARAMETERS_META_DATA_KEY);
315: }
316:
317: // meta data key to store PageParameters in page instance. This is used to
318: // save the PageParameters that were
319: // used to create the page instance so that later we can used them when
320: // generating page URL
321: private static final PageParametersMetaDataKey PAGE_PARAMETERS_META_DATA_KEY = new PageParametersMetaDataKey();
322:
323: private static class PageParametersMetaDataKey extends MetaDataKey {
324: private static final long serialVersionUID = 1L;
325:
326: /**
327: * Construct.
328: */
329: public PageParametersMetaDataKey() {
330: super (PageParameters.class);
331: }
332: };
333:
334: // used to store number of traling slashes in page url (prior the PageInfo)
335: // part. This is necessary to maintain
336: // the exact number of slashes after page redirect, so that we don't break
337: // things that rely on URL depth
338: // (other mounted URLs)
339: private static final OriginalUrlTrailingSlashesCountMetaDataKey ORIGINAL_TRAILING_SLASHES_COUNT_METADATA_KEY = new OriginalUrlTrailingSlashesCountMetaDataKey();
340:
341: private static class OriginalUrlTrailingSlashesCountMetaDataKey
342: extends MetaDataKey {
343: private static final long serialVersionUID = 1L;
344:
345: /**
346: * Construct.
347: */
348: public OriginalUrlTrailingSlashesCountMetaDataKey() {
349: super (Integer.class);
350: }
351:
352: }
353:
354: /**
355: * Fix the amount of traling slashes in the speciffied buffer.
356: *
357: * @param buffer
358: * @param desiredCount
359: */
360: private void fixTrailingSlashes(AppendingStringBuffer buffer,
361: int desiredCount) {
362: int current = getTrailingSlashesCount(buffer);
363: if (current > desiredCount) {
364: buffer
365: .setLength(buffer.length()
366: - (current - desiredCount));
367: } else if (desiredCount > current) {
368: int toAdd = desiredCount - current;
369: while (toAdd > 0) {
370: buffer.append("/");
371: --toAdd;
372: }
373: }
374: }
375:
376: /**
377: * @see org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy#encode(org.apache.wicket.IRequestTarget)
378: */
379: public CharSequence encode(IRequestTarget requestTarget) {
380: if (matches(requestTarget) == false) {
381: throw new IllegalArgumentException(
382: "Unsupported request target type.");
383: }
384:
385: PageParameters parameters = getPageParameters(requestTarget);
386: PageInfo pageInfo = getPageInfo(requestTarget);
387:
388: final AppendingStringBuffer url = new AppendingStringBuffer(40);
389: url.append(getMountPath());
390: appendParameters(url, parameters);
391:
392: // check whether we know if the initial URL ended with slash
393: Integer trailingSlashesCount = getOriginalOriginalTrailingSlashesCount(requestTarget);
394: if (trailingSlashesCount != null) {
395: fixTrailingSlashes(url, trailingSlashesCount.intValue());
396: }
397:
398: return addPageInfo(url.toString(), pageInfo);
399: }
400:
401: /**
402: * @see org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy#matches(org.apache.wicket.IRequestTarget)
403: */
404: public boolean matches(IRequestTarget requestTarget) {
405: if (requestTarget instanceof BookmarkablePageRequestTarget) {
406: BookmarkablePageRequestTarget target = (BookmarkablePageRequestTarget) requestTarget;
407: return target.getPageClass().equals(pageClassRef.get());
408: } else if (requestTarget instanceof ListenerInterfaceRequestTarget) {
409: ListenerInterfaceRequestTarget target = (ListenerInterfaceRequestTarget) requestTarget;
410: return target.getPage().getClass().equals(
411: pageClassRef.get())
412: && target.getRequestListenerInterface().equals(
413: IRedirectListener.INTERFACE);
414: }
415: return false;
416: }
417:
418: /**
419: * Class that encapsulates {@link PageInfo} instance and the URL part prior
420: * the PageInfo part
421: *
422: * @author Matej Knopp
423: */
424: protected static class PageInfoExtraction {
425: private final String urlAfterExtraction;
426:
427: private final PageInfo pageInfo;
428:
429: /**
430: * Construct.
431: *
432: * @param urlAfterExtraction
433: * @param pageInfo
434: */
435: public PageInfoExtraction(String urlAfterExtraction,
436: PageInfo pageInfo) {
437: this .urlAfterExtraction = urlAfterExtraction;
438: this .pageInfo = pageInfo;
439: }
440:
441: /**
442: * @return
443: */
444: public PageInfo getPageInfo() {
445: return pageInfo;
446: }
447:
448: /**
449: * @return
450: */
451: public String getUrlAfterExtraction() {
452: return urlAfterExtraction;
453: }
454: }
455:
456: /**
457: * Extracts the PageInfo string.
458: *
459: * @param url
460: * @return
461: */
462: protected PageInfoExtraction extractPageInfo(String url) {
463: int begin = url.lastIndexOf(getBeginSeparator());
464: PageInfo last = null;
465: String lastSubstring = "";
466: while (begin != -1) {
467: lastSubstring = url.substring(begin);
468: if (lastSubstring.length() > getBeginSeparator().length()
469: + getEndSeparator().length()
470: && lastSubstring.startsWith(getBeginSeparator())
471: && lastSubstring.endsWith(getEndSeparator())) {
472: String pageInfoString = lastSubstring.substring(
473: getBeginSeparator().length(), //
474: lastSubstring.length()
475: - getEndSeparator().length());
476: PageInfo info = PageInfo.parsePageInfo(pageInfoString);
477: last = info;
478: }
479: begin = url.lastIndexOf(getBeginSeparator(), begin - 1);
480: }
481: return new PageInfoExtraction(url.substring(0, url.length()
482: - lastSubstring.length()), last);
483: }
484:
485: protected String getBeginSeparator() {
486: return ".";
487: }
488:
489: protected String getEndSeparator() {
490: return "";
491: }
492:
493: /**
494: * Encodes the PageInfo part to the URL
495: *
496: * @param url
497: * @param pageInfo
498: * @return
499: */
500: protected String addPageInfo(String url, PageInfo pageInfo) {
501: if (pageInfo != null) {
502: return url + getBeginSeparator() + pageInfo.toString()
503: + getEndSeparator();
504: } else {
505: return url;
506: }
507: }
508:
509: /**
510: * Possible string representation of PageInfo:
511: * <ul>
512: * <li>pageId
513: * <li>pageId.version
514: * <li>pageMap (only if pageMap starts with a letter)
515: * <li>.pageMap
516: * <li>pageMap.pageId.version
517: * <li>pageMap.pageId (only if pageMap name starts with a letter)
518: * </ul>
519: *
520: * @author Matej Knopp
521: */
522: protected static class PageInfo {
523: private final Integer pageId;
524: private final Integer versionNumber;
525: private final String pageMapName;
526:
527: /**
528: * Construct.
529: *
530: * @param pageId
531: * @param versionNumber
532: * @param pageMapName
533: */
534: public PageInfo(Integer pageId, Integer versionNumber,
535: String pageMapName) {
536: if ((pageId == null && (versionNumber != null || pageMapName == null))
537: || (versionNumber == null && (pageId != null || pageMapName == null))) {
538: throw new IllegalArgumentException(
539: "Either both pageId and versionNumber must be null or none of them.");
540: }
541: this .pageId = pageId;
542: this .versionNumber = versionNumber;
543: this .pageMapName = pageMapName;
544: }
545:
546: /**
547: * @return
548: */
549: public Integer getPageId() {
550: return pageId;
551: }
552:
553: /**
554: * @return
555: */
556: public Integer getVersionNumber() {
557: return versionNumber;
558: }
559:
560: /**
561: * @return
562: */
563: public String getPageMapName() {
564: return pageMapName;
565: }
566:
567: private static char getPageInfoSeparator() {
568: return '.';
569: }
570:
571: /**
572: * <ul>
573: * <li>pageId
574: * <li>pageId.version
575: * <li>pageMap (only in if pagemap strats with a letter)
576: * <li>.pageMap
577: * <li>pageMap.pageId (only in if pageMap name starts with a letter)
578: * <li>pageMap.pageId.version
579: * </ul>
580: */
581: public String toString() {
582: String pageMapName = this .pageMapName;
583:
584: // we don't need to encode the pageMapName when the pageId is unique
585: // per session
586: if (pageMapName != null
587: && pageId != null
588: && Application.exists()
589: && Application.get().getSessionSettings()
590: .isPageIdUniquePerSession()) {
591: pageMapName = null;
592: }
593:
594: AppendingStringBuffer buffer = new AppendingStringBuffer(5);
595:
596: final boolean pmEmpty = Strings.isEmpty(pageMapName);
597: final boolean pmContainsLetter = !pmEmpty
598: && !isNumber(pageMapName);
599:
600: if (pageId != null && pmEmpty
601: && versionNumber.intValue() == 0) {
602: // pageId
603: buffer.append(pageId);
604: } else if (pageId != null && pmEmpty
605: && versionNumber.intValue() != 0) {
606: // pageId.version
607: buffer.append(pageId);
608: buffer.append(getPageInfoSeparator());
609: buffer.append(versionNumber);
610: } else if (pageId == null && pmContainsLetter) {
611: // pageMap (must start with letter)
612: buffer.append(pageMapName);
613: } else if (pageId == null && !pmEmpty && !pmContainsLetter) {
614: // .pageMap
615: buffer.append(getPageInfoSeparator());
616: buffer.append(pageMapName);
617: } else if (pmContainsLetter && pageId != null
618: && versionNumber.intValue() == 0) {
619: // pageMap.pageId (pageMap must start with a letter)
620: buffer.append(pageMapName);
621: buffer.append(getPageInfoSeparator());
622: buffer.append(pageId);
623: } else if (!pmEmpty && pageId != null) {
624: // pageMap.pageId.pageVersion
625: buffer.append(pageMapName);
626: buffer.append(getPageInfoSeparator());
627: buffer.append(pageId);
628: buffer.append(getPageInfoSeparator());
629: buffer.append(versionNumber);
630: }
631:
632: return buffer.toString();
633: }
634:
635: /**
636: * Method that rigidly checks if the string consists of digits only.
637: *
638: * @param string
639: * @return
640: */
641: private static boolean isNumber(String string) {
642: if (string == null || string.length() == 0) {
643: return false;
644: }
645: for (int i = 0; i < string.length(); ++i) {
646: if (Character.isDigit(string.charAt(i)) == false) {
647: return false;
648: }
649: }
650: return true;
651: }
652:
653: /**
654: * <ul>
655: * <li>pageId
656: * <li>pageId.version
657: * <li>pageMap (only in if pagemap strats with a letter)
658: * <li>.pageMap
659: * <li>pageMap.pageId (only in if pageMap name starts with a letter)
660: * <li>pageMap.pageId.version
661: * </ul>
662: *
663: * @param src
664: * @return
665: */
666: public static PageInfo parsePageInfo(String src) {
667: if (src == null || src.length() == 0) {
668: return null;
669: }
670:
671: String segments[] = Strings.split(src,
672: getPageInfoSeparator());
673:
674: if (segments.length > 3) {
675: return null;
676: }
677:
678: if (segments.length == 1 && isNumber(segments[0])) {
679: // pageId
680: return new PageInfo(Integer.valueOf(segments[0]),
681: new Integer(0), null);
682: } else if (segments.length == 2 && isNumber(segments[0])
683: && isNumber(segments[1])) {
684: // pageId:pageVersion
685: return new PageInfo(Integer.valueOf(segments[0]),
686: Integer.valueOf(segments[1]), null);
687: } else if (segments.length == 1 && !isNumber(segments[0])) {
688: // pageMap (starts with leter)
689: return new PageInfo(null, null, segments[0]);
690: } else if (segments.length == 2
691: && segments[0].length() == 0) {
692: // .pageMap
693: return new PageInfo(null, null, segments[1]);
694: } else if (segments.length == 2 && !isNumber(segments[0])) {
695: // pageMap.pageId (pageMap starts with letter)
696: return new PageInfo(Integer.valueOf(segments[1]),
697: new Integer(0), segments[0]);
698: } else if (segments.length == 3) {
699: if (segments[2].length() == 0 && isNumber(segments[1])) {
700: // we don't encode it like this, but we still should be able
701: // to parse it
702: // pageMapName.pageId.
703: return new PageInfo(Integer.valueOf(segments[1]),
704: new Integer(0), segments[0]);
705: } else if (isNumber(segments[1])
706: && isNumber(segments[2])) {
707: // pageMapName.pageId.pageVersion
708: return new PageInfo(Integer.valueOf(segments[1]),
709: Integer.valueOf(segments[2]), segments[0]);
710: }
711: }
712:
713: return null;
714: }
715:
716: };
717:
718: /**
719: * BookmarkablePage request target that does a redirect after bookmarkable
720: * page was rendered (only if the bookmarkable page is stateful though)
721: *
722: * @author Matej Knopp
723: */
724: private static class HybridBookmarkablePageRequestTarget extends
725: BookmarkablePageRequestTarget {
726: private final int originalUrlTrailingSlashesCount;
727: private final boolean redirect;
728:
729: /**
730: * Construct.
731: *
732: * @param pageMapName
733: * @param pageClass
734: * @param pageParameters
735: * @param originalUrlTrailingSlashesCount
736: * @param redirect
737: */
738: public HybridBookmarkablePageRequestTarget(String pageMapName,
739: Class pageClass, PageParameters pageParameters,
740: int originalUrlTrailingSlashesCount, boolean redirect) {
741: super (pageMapName, pageClass, pageParameters);
742: this .originalUrlTrailingSlashesCount = originalUrlTrailingSlashesCount;
743: this .redirect = redirect;
744: }
745:
746: protected Page newPage(Class pageClass,
747: RequestCycle requestCycle) {
748: Page page = super .newPage(pageClass, requestCycle);
749: page.setMetaData(PAGE_PARAMETERS_META_DATA_KEY,
750: getPageParameters());
751: page.setMetaData(
752: ORIGINAL_TRAILING_SLASHES_COUNT_METADATA_KEY,
753: new Integer(originalUrlTrailingSlashesCount));
754: return page;
755: }
756:
757: public void respond(RequestCycle requestCycle) {
758: {
759: Page page = getPage(requestCycle);
760: if (page.isPageStateless() == false && redirect) {
761: requestCycle.redirectTo(page);
762: } else {
763: super .respond(requestCycle);
764: }
765: }
766: }
767: };
768:
769: /**
770: * @see org.apache.wicket.request.target.coding.AbstractRequestTargetUrlCodingStrategy#matches(java.lang.String)
771: */
772: public boolean matches(String path) {
773: if (path.startsWith(getMountPath())) {
774: /*
775: * We need to match /mount/point or /mount/point/with/extra/path,
776: * but not /mount/pointXXX
777: */
778: String remainder = path.substring(getMountPath().length());
779: if (remainder.length() == 0 || remainder.startsWith("/")) {
780: return true;
781: }
782: /*
783: * We also need to accept /mount/point(XXX)
784: */
785: if (remainder.length() > getBeginSeparator().length()
786: + getEndSeparator().length()
787: && remainder.startsWith(getBeginSeparator())
788: && remainder.endsWith(getEndSeparator())) {
789: String substring = remainder
790: .substring(getBeginSeparator().length(), //
791: remainder.length()
792: - getEndSeparator().length());
793: PageInfo info = PageInfo.parsePageInfo(substring);
794: if (info != null) {
795: return true;
796: }
797: }
798: }
799: return false;
800: }
801:
802: public String toString() {
803: return "HybridUrlCodingStrategy[page=" + pageClassRef.get()
804: + "]";
805: }
806:
807: }
|