All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fitnesse.wiki.fs.WikiFilePage Maven / Gradle / Ivy

There is a newer version: 20240707
Show newest version
package fitnesse.wiki.fs;

import fitnesse.wiki.*;
import fitnesse.wikitext.parser.*;
import org.apache.commons.lang3.StringUtils;
import util.FileUtil;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

import static fitnesse.util.StringUtils.isBlank;
import static java.lang.String.format;

/**
 * With this page all content is saved in one file: WikiPageName.wiki.
 * Sub wiki's are stored as WikiPageMame/SubWiki.wiki.
 * This format should eventually replace the {@link FileSystemPage}.
 */
public class WikiFilePage extends BaseWikitextPage implements FileBasedWikiPage {
  public static final String FILE_EXTENSION = ".wiki";
  public static final String ROOT_FILE_NAME = "_root" + FILE_EXTENSION;
  private static final SymbolProvider WIKI_FILE_PARSING_PROVIDER = new SymbolProvider( new SymbolType[] {
    FrontMatter.symbolType, SymbolType.Text});

  private final File path;
  private final VersionsController versionsController;
  private final SubWikiPageFactory subWikiPageFactory;
  private final String versionName;
  private PageData pageData;

  protected WikiFilePage(final File path, final String name, final WikiPage parent,
                      final String versionName, final VersionsController versionsController,
                      final SubWikiPageFactory subWikiPageFactory, final VariableSource variableSource) {
    super(name, parent, variableSource);
    this.path = path;
    this.versionsController = versionsController;
    this.subWikiPageFactory = subWikiPageFactory;
    this.versionName = versionName;
  }

  private WikiFilePage(WikiFilePage page, String versionName) {
    this(page.path, page.getName(), (page.isRoot() ? null : page.getParent()), versionName,
      page.versionsController, page.subWikiPageFactory, page.getVariableSource());
  }

  @Override
  public WikiPage addChildPage(final String childName) {
    return new WikiFilePage(new File(getFileSystemPath(), childName + FILE_EXTENSION), childName, this, null, this.versionsController, this.subWikiPageFactory, this.getVariableSource());
  }

  private File getSubWikiFolder() {
    return path;
  }

  @Override
  public WikiPage getChildPage(final String childName) {
    return subWikiPageFactory.getChildPage(this, childName);
  }

  @Override
  public void removeChildPage(final String name) {
    final WikiPage childPage = getChildPage(name);
    if (childPage != null) {
      childPage.remove();
    }
  }

  @Override
  public void remove() {
    try {
      versionsController.delete(path, getFileSystemPath());
    } catch (IOException e) {
      throw new WikiPageLoadException(format("Could not remove page %s", new WikiPagePath(this).toString()), e);
    }
  }

  @Override
  public List getChildren() {
    return subWikiPageFactory.getChildren(this);
  }

  @Override
  public PageData getData() {
    if (pageData == null) {
      try {
        pageData = getDataVersion();
      } catch (IOException e) {
        throw new WikiPageLoadException("Could not load page data for page " + path.getPath(), e);
      }
    }
    return new PageData(pageData);
  }

  @Override
  public Collection getVersions() {
    return versionsController.history(path);
  }

  @Override
  public WikiPage getVersion(final String versionName) {
    try {
      versionsController.getRevisionData(versionName, path);
    } catch (IOException e) {
      throw new WikiPageLoadException(format("Could not load version %s for page at %s", versionName, path.getPath()), e);
    }
    return new WikiFilePage(this, versionName);
  }

  @Override
  public VersionInfo commit(final PageData data) {
    resetCache();
    try {
      return versionsController.makeVersion(new WikiFilePageVersion(data));
    } catch (IOException e) {
      throw new WikiPageLoadException(e);
    }
  }

  @Override
  public File getFileSystemPath() {
    if (ROOT_FILE_NAME.equals(path.getName())) {
      return path.getParentFile();
    } else {
      String pathStr = this.path.getPath();
      return new File(pathStr.substring(0, pathStr.length() - FILE_EXTENSION.length()));
    }
  }

  @Override
  protected void resetCache() {
    super.resetCache();
    pageData = null;
  }

  private PageData getDataVersion() throws IOException {
    FileVersion[] versions = versionsController.getRevisionData(versionName, path);
    FileVersion fileVersion = versions[0];
    String content = "";
    WikiPageProperty properties = defaultPageProperties();

    if (fileVersion != null) {
      try {
        String fileContent = loadContent(fileVersion);

        final ParsingPage parsingPage = makeParsingPage(this);
        final Symbol syntaxTree = Parser.make(parsingPage, fileContent, WIKI_FILE_PARSING_PROVIDER).parse();
        if (!syntaxTree.getChildren().isEmpty()) {
          final Symbol maybeFrontMatter = syntaxTree.getChildren().get(0);
          if (maybeFrontMatter.isType(FrontMatter.symbolType)) {
            properties = mergeWikiPageProperties(properties, maybeFrontMatter);
            if (syntaxTree.getChildren().size() > 1) {
              content = fileContent.substring(maybeFrontMatter.getEndOffset());
            }
          } else {
            content = fileContent;
          }
        }
        properties.setLastModificationTime(fileVersion.getLastModificationTime());

      } catch (IOException e) {
        throw new WikiPageLoadException(e);
      }
    }
    pageData = new PageData(content, properties);
    return pageData;
  }

  private WikiPageProperty mergeWikiPageProperties(final WikiPageProperty properties, final Symbol frontMatter) {
    for (Symbol keyValue : frontMatter.getChildren()) {
      if (keyValue.isType(FrontMatter.keyValueSymbolType)) {
        String key = keyValue.getChildren().get(0).getContent();
        String value = keyValue.getChildren().get(1).getContent();
        if (isBooleanProperty(key)) {
          if (isBlank(value) || isTruthy(value)) {
            properties.set(key, value);
          } else if (isFalsy(value)) {
            properties.remove(key);
          }
        } else {
          WikiPageProperty symLinks = properties.set(key, value);
          for (int i = 2; i < keyValue.getChildren().size(); i++) {
            final Symbol subProperty = keyValue.getChildren().get(i);
            assert subProperty.isType(FrontMatter.keyValueSymbolType);
            String linkName = subProperty.getChildren().get(0).getContent();
            String linkPath = subProperty.getChildren().get(1).getContent();
            symLinks.set(linkName, linkPath);
          }
        }
      }
    }
    return properties;
  }

  private boolean isBooleanProperty(final String key) {
    return qualifiesAs(key, PageData.PAGE_TYPE_ATTRIBUTES) ||
      qualifiesAs(key, PageData.NON_SECURITY_ATTRIBUTES) ||
      qualifiesAs(key, PageData.SECURITY_ATTRIBUTES);
  }

  private boolean isTruthy(final String value) {
    return qualifiesAs(value.toLowerCase(), new String[]{"y", "yes", "t", "true", "1"});
  }

  private boolean isFalsy(final String value) {
    return qualifiesAs(value.toLowerCase(), new String[]{"n", "no", "f", "false", "0"});
  }

  private boolean qualifiesAs(final String value, final String[] qualifiers) {
    for (String q : qualifiers) {
      if (q.equals(value)) return true;
    }
    return false;
  }

  private String loadContent(final FileVersion fileVersion) throws IOException {
    try (InputStream content = fileVersion.getContent()) {
      return FileUtil.toString(content);
    }
  }

  private String propertiesYaml(final WikiPageProperty pageProperties) {
    final WikiPageProperty defaultProperties = defaultPageProperties();
    final List lines = new ArrayList<>();
    for (String key : pageProperties.keySet()) {
      if (isBooleanProperty(key)) {
        if (!defaultProperties.has(key)) {
          lines.add(key);
        }
      } else if (!WikiPageProperty.LAST_MODIFIED.equals(key)) {
        final StringBuilder builder = new StringBuilder();
        builder.append(key);
        if (!isBlank(pageProperties.get(key))) {
          builder.append(": ").append(pageProperties.get(key));
        }
        final WikiPageProperty subProperty = pageProperties.getProperty(key);
        for (String pageName: subProperty.keySet()) {
          builder.append("\n  ").append(pageName).append(": ").append(subProperty.get(pageName));
        }
        lines.add(builder.toString());
      }
    }
    for (String key : defaultProperties.keySet()) {
        if (isBooleanProperty(key) && !pageProperties.has(key)) {
          lines.add(key + ": no");
        }
    }
    Collections.sort(lines);
    return lines.isEmpty() ? "" : "---\n" + StringUtils.join(lines, '\n') + "\n---\n";
  }

  private class WikiFilePageVersion implements FileVersion {
    private final PageData data;

    public WikiFilePageVersion(final PageData data) {
      this.data = new PageData(data);
    }

    @Override
    public File getFile() {
      return path;
    }

    @Override
    public InputStream getContent() throws IOException {
      String yaml = propertiesYaml(data.getProperties());
      final String content = yaml + data.getContent();
      return new ByteArrayInputStream(content.getBytes(FileUtil.CHARENCODING));
    }

    @Override
    public String getAuthor() {
      return data.getAttribute(WikiPageProperty.LAST_MODIFYING_USER);
    }

    @Override
    public Date getLastModificationTime() {
      return data.getProperties().getLastModificationTime();
    }
  }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy