// 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; import java.util.Iterator; import java.util.List; import java.util.ArrayList; public class PageCrawlerImpl implements PageCrawler { private PageCrawlerDeadEndStrategy deadEndStrategy; protected PageCrawlerImpl() { } public WikiPage getPage(WikiPage context, WikiPagePath path) { if (path == null) return null; if (isRoot(path)) return getRoot(context); if (path.isEmpty()) return context; if (path.isAbsolute()) { WikiPagePath relativeToRoot = new WikiPagePath(path); relativeToRoot.setPathMode(WikiPagePath.Mode.RELATIVE); return getPage(getRoot(context), relativeToRoot); } else if (path.isBackwardSearchPath()) return getSiblingPage(context, path); String firstPathElement = path.getFirst(); WikiPagePath restOfPath = path.getRest(); WikiPage childPage = context.getChildPage(firstPathElement); if (childPage != null) return getPage(childPage, restOfPath); else return getPageAfterDeadEnd(context, firstPathElement, restOfPath); } private boolean isRoot(WikiPagePath path) { return path.isAbsolute() && path.isEmpty(); } protected WikiPage getPageAfterDeadEnd(WikiPage context, String first, WikiPagePath rest) { rest.addNameToFront(first); if (deadEndStrategy != null) return deadEndStrategy.getPageAfterDeadEnd(context, rest, this); else return null; } public void setDeadEndStrategy(PageCrawlerDeadEndStrategy strategy) { deadEndStrategy = strategy; } public boolean pageExists(WikiPage context, WikiPagePath path) { return getPage(context, path) != null; } public WikiPagePath getFullPathOfChild(WikiPage parent, WikiPagePath childPath) { WikiPagePath fullPathOfChild; if (childPath.isAbsolute()) fullPathOfChild = childPath.relativePath(); else { WikiPagePath absolutePathOfParent = new WikiPagePath(parent); fullPathOfChild = absolutePathOfParent.append(childPath); } return fullPathOfChild; } public WikiPagePath getFullPath(WikiPage page) { return new WikiPagePath(page); } public WikiPage addPage(WikiPage context, WikiPagePath path, String content) { WikiPage page = addPage(context, path); if (page != null) { PageData data = new PageData(page); data.setContent(content); page.commit(data); } return page; } public WikiPage addPage(WikiPage context, WikiPagePath path) { return getOrMakePage(context, path.getNames()); } private WikiPage getOrMakePage(WikiPage context, List<?> namePieces) { String first = (String) namePieces.get(0); List<?> rest = namePieces.subList(1, namePieces.size()); WikiPage current; if (context.getChildPage(first) == null) current = context.addChildPage(first); else current = context.getChildPage(first); if (rest.size() == 0) return current; return getOrMakePage(current, rest); } public String getRelativeName(WikiPage base, WikiPage page) { StringBuffer qualName = new StringBuffer(); for (WikiPage p = page; !isRoot(p) && p != base; p = p.getParent()) { if (p != page) qualName.insert(0, "."); qualName.insert(0, p.getName()); } return qualName.toString(); } //TODO this doesn't belong here public static WikiPage getClosestInheritedPage(String pageName, WikiPage context) { List<WikiPage> ancestors = WikiPageUtil.getAncestorsStartingWith(context); for (WikiPage ancestor : ancestors) { WikiPage namedPage = ancestor.getChildPage(pageName); if (namedPage != null) return namedPage; } return null; } public boolean isRoot(WikiPage page) { WikiPage parent = page.getParent(); return parent == null || parent == page; } public WikiPage getRoot(WikiPage page) { if (isRoot(page)) return page; else return getRoot(page.getParent()); } public void traverse(WikiPage context, TraversalListener listener) { if (context.getClass() == SymbolicPage.class) return; //TODO MdM Catch any exception thrown by the following and add the page name to the Exception message. listener.process(context); List<?> children = context.getChildren(); for (Iterator<?> iterator = children.iterator(); iterator.hasNext();) { WikiPage wikiPage = (WikiPage) iterator.next(); 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. */ public WikiPage getSiblingPage(WikiPage page, WikiPagePath pathRelativeToSibling) { PageCrawler crawler = page.getPageCrawler(); if (pathRelativeToSibling.isSubPagePath()) { WikiPagePath relativePath = new WikiPagePath(pathRelativeToSibling); relativePath.setPathMode(WikiPagePath.Mode.RELATIVE); return getPage(page, relativePath); } else if (pathRelativeToSibling.isBackwardSearchPath()) { String target = pathRelativeToSibling.getFirst(); WikiPage ancestor = findAncestorWithName(page, target); if (ancestor != null) return getPage(ancestor, pathRelativeToSibling.getRest()); WikiPagePath absolutePath = new WikiPagePath(pathRelativeToSibling); absolutePath.makeAbsolute(); return getPage(crawler.getRoot(page), absolutePath); } else { WikiPage parent = page.getParent(); return getPage(parent, pathRelativeToSibling); } } public WikiPage findAncestorWithName(WikiPage page, String name) { for (WikiPage current = page.getParent(); !isRoot(current); current = current.getParent()) { if (current.getName().equals(name)) return current; } return null; } public static List<WikiPage> getAllUncles(String uncleName, WikiPage nephew) { List<WikiPage> uncles = new ArrayList<WikiPage>(); List<WikiPage> ancestors = WikiPageUtil.getAncestorsStartingWith(nephew); for (WikiPage ancestor : ancestors) { WikiPage namedPage = ancestor.getChildPage(uncleName); if (namedPage != null) uncles.add(namedPage); } return uncles; } }