// 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;
}
}