// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.wiki; import fitnesse.components.TraversalListener; public class PageCrawlerImpl implements PageCrawler { private final WikiPage context; public PageCrawlerImpl(WikiPage context) { this.context = context; } @Override public WikiPage getPage(WikiPagePath path) { return getPage(path, null); } @Override public WikiPage getPage(WikiPagePath path, PageCrawlerDeadEndStrategy deadEndStrategy) { return getPage(context, path, deadEndStrategy); } private WikiPage getPage(WikiPage page, WikiPagePath path, PageCrawlerDeadEndStrategy deadEndStrategy) { if (path == null) return null; if (isRoot(path)) return getRoot(page); if (path.isEmpty()) return page; if (path.isAbsolute()) { WikiPagePath relativeToRoot = new WikiPagePath(path); relativeToRoot.setPathMode(WikiPagePath.Mode.RELATIVE); return getPage(getRoot(page), relativeToRoot, deadEndStrategy); } else if (path.isBackwardSearchPath()) return getSiblingPage(page, path); String firstPathElement = path.getFirst(); WikiPagePath restOfPath = path.getRest(); WikiPage childPage = page.getChildPage(firstPathElement); if (childPage != null) return getPage(childPage, restOfPath, deadEndStrategy); else return getPageAfterDeadEnd(page, firstPathElement, restOfPath, deadEndStrategy); } private boolean isRoot(WikiPagePath path) { return path.isAbsolute() && path.isEmpty(); } private WikiPage getPageAfterDeadEnd(WikiPage page, String first, WikiPagePath rest, PageCrawlerDeadEndStrategy deadEndStrategy) { rest.addNameToFront(first); if (deadEndStrategy != null) return deadEndStrategy.getPageAfterDeadEnd(page, rest, this); else return null; } @Override public boolean pageExists(WikiPagePath path) { return getPage(path) != null; } @Override public WikiPagePath getFullPathOfChild(WikiPagePath childPath) { WikiPagePath fullPathOfChild; if (childPath.isAbsolute()) fullPathOfChild = childPath.relativePath(); else { WikiPagePath absolutePathOfParent = new WikiPagePath(context); fullPathOfChild = absolutePathOfParent.append(childPath); } return fullPathOfChild; } @Override public WikiPagePath getFullPath() { return new WikiPagePath(context); } @Override public String getRelativeName(WikiPage page) { StringBuilder name = new StringBuilder(); for (WikiPage p = page; !p.isRoot() && !p.equals(context); p = p.getParent()) { if (p != page) name.insert(0, "."); name.insert(0, p.getName()); } return name.toString(); } @Override public WikiPage getClosestInheritedPage(final String pageName) { final WikiPage[] foundPage = new WikiPage[1]; traversePageAndAncestors(new TraversalListener<WikiPage>() { @Override public void process(WikiPage page) { WikiPage namedPage = page.getChildPage(pageName); if (namedPage != null && foundPage[0] == null) foundPage[0] = namedPage; } }); return foundPage[0]; } @Override public WikiPage getRoot() { return getRoot(context); } private WikiPage getRoot(WikiPage page) { if (page.isRoot()) return page; else return getRoot(page.getParent()); } @Override public void traverse(TraversalListener<? super WikiPage> listener) { traverse(context, listener); } private void traverse(WikiPage page, TraversalListener<? super WikiPage> listener) { listener.process(page); for (WikiPage wikiPage : page.getChildren()) { traverse(wikiPage, listener); } } /* Todo: RcM. All calls to getPage should actually come here, and be relative to the current page, not the parent page. It was a gross error to have the whole wiki know that references were relative to the parent instead of the page. */ @Override public WikiPage getSiblingPage(WikiPagePath pathRelativeToSibling) { return getSiblingPage(context, pathRelativeToSibling); } private WikiPage getSiblingPage(WikiPage page, WikiPagePath pathRelativeToSibling) { if (pathRelativeToSibling.isSubPagePath()) { WikiPagePath relativePath = new WikiPagePath(pathRelativeToSibling); relativePath.setPathMode(WikiPagePath.Mode.RELATIVE); return getPage(relativePath); } else if (pathRelativeToSibling.isBackwardSearchPath()) { String target = pathRelativeToSibling.getFirst(); WikiPage ancestor = findAncestorWithName(target); if (ancestor != null) return getPage(ancestor, pathRelativeToSibling.getRest(), null); WikiPagePath absolutePath = new WikiPagePath(pathRelativeToSibling); absolutePath.makeAbsolute(); WikiPage root = getRoot(page); return getPage(root, absolutePath, null); } else { WikiPage parent = page.getParent(); return getPage(parent, pathRelativeToSibling, null); } } @Override public WikiPage findAncestorWithName(String name) { for (WikiPage current = context.getParent(); !current.isRoot(); current = current.getParent()) { if (current.getName().equals(name)) return current; } return null; } @Override public void traverseUncles(final String uncleName, final TraversalListener<? super WikiPage> callback) { traversePageAndAncestors(new TraversalListener<WikiPage>() { @Override public void process(WikiPage page) { WikiPage namedPage = page.getChildPage(uncleName); if (namedPage != null) callback.process(namedPage); } }); } @Override public void traversePageAndAncestors(TraversalListener<? super WikiPage> callback) { WikiPage page = context; while (!page.isRoot()) { callback.process(page); page = page.getParent(); } // Call once more for root page. callback.process(page); } }