/* * Copyright 2015 Pawan Dubey [email protected]. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.pawandubey.griffin; import static com.pawandubey.griffin.Configurator.LINE_SEPARATOR; import static com.pawandubey.griffin.Data.config; import static com.pawandubey.griffin.Data.fileQueue; import com.pawandubey.griffin.cache.Cacher; import com.pawandubey.griffin.cli.GriffinCommand; import com.pawandubey.griffin.cli.NewCommand; import com.pawandubey.griffin.cli.PreviewCommand; import com.pawandubey.griffin.cli.PublishCommand; import com.pawandubey.griffin.model.Parsable; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileSystemException; import java.nio.file.Files; import java.nio.file.NotDirectoryException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; import java.util.logging.Logger; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.Option; import org.kohsuke.args4j.ParserProperties; import org.kohsuke.args4j.spi.BooleanOptionHandler; import org.kohsuke.args4j.spi.SubCommand; import org.kohsuke.args4j.spi.SubCommandHandler; import org.kohsuke.args4j.spi.SubCommands; /** * * @author Pawan Dubey [email protected] */ public class Griffin { private final DirectoryCrawler crawler; private Parser parser; private Cacher cacher; @Option(name = "--version", aliases = {"-v"}, handler = BooleanOptionHandler.class, usage = "print the current version") private boolean version = false; @Option(name = "--help", aliases = {"-h"}, handler = BooleanOptionHandler.class, usage = "print help message for the command") private boolean help = false; @Argument(usage = "Execute subcommands", metaVar = "<commands>", handler = SubCommandHandler.class) @SubCommands({ @SubCommand(name = "new", impl = NewCommand.class), @SubCommand(name = "publish", impl = PublishCommand.class), @SubCommand(name = "preview", impl = PreviewCommand.class) }) public GriffinCommand commands; /** * Creates a new instance of Griffin */ public Griffin() { cacher = new Cacher(); crawler = new DirectoryCrawler(); } /** * Creates a new instance of Griffin with the root directory of the site set * to the given path. * * @param path The path to the root directory of the griffin site. */ public Griffin(Path path) { crawler = new DirectoryCrawler(path.toString()); } /** * Creates(scaffolds out) a new Griffin directory at the given path. * * @param path The path at which to scaffold. * @param name The name to give to the directory * @throws IOException the exception * @throws java.net.URISyntaxException */ public void initialize(Path path, String name) throws IOException, URISyntaxException { checkPathValidity(path, name); initializeConfigurationSettings(path, name); Initializer init = new Initializer(); init.scaffold(path, name); Data.config.writeConfig(path.resolve(name)); } /** * Parses the content of the site in the 'content' directory and produces * the output. It parses incrementally i.e it only parses the content which * has changed since the last parsing event if the fastParse variable is * true. * * @param fastParse Do a fast incremental parse * @param rebuild Do force a full rebuild * @throws IOException the exception * @throws InterruptedException the exception */ public void publish(boolean fastParse, boolean rebuild) throws IOException, InterruptedException { long start = System.currentTimeMillis(); InfoHandler info = new InfoHandler(); if (cacher.cacheExists() && !rebuild) { System.out.println("Reading from the cache for your pleasure..."); ConcurrentMap<String, Object> map = cacher.readFromCacheIfExists(); ConcurrentMap<String, List<Parsable>> tag = (ConcurrentMap<String, List<Parsable>>) map.get("tags"); BlockingQueue<Parsable> qu = (BlockingQueue<Parsable>) map.get("fileQueue"); Data.fileQueue.addAll(qu); Data.tags.putAll(tag); int st = Data.fileQueue.size(); System.out.println("Read " + st + " objects from the cache. Woohooo!!"); crawler.fastReadIntoQueue(Paths.get(DirectoryCrawler.SOURCE_DIRECTORY).normalize()); System.out.println("Found " + (Data.fileQueue.size() - st) + " new objects!"); } else { if (fastParse && !rebuild) { crawler.fastReadIntoQueue(Paths.get(DirectoryCrawler.SOURCE_DIRECTORY).normalize()); } else { System.out.println("Rebuilding site from scratch..."); crawler.readIntoQueue(Paths.get(DirectoryCrawler.SOURCE_DIRECTORY).normalize()); } } info.findLatestPosts(fileQueue); info.findNavigationPages(fileQueue); cacher.cacheFileQueue(); System.out.println("Parsing " + Data.fileQueue.size() + " objects..."); parser = new Parser(); parser.parse(fileQueue); info.writeInfoFile(); parser.shutDownExecutors(); cacher.cacheTaggedParsables(); long end = System.currentTimeMillis(); System.out.println("Time (hardly) taken: " + (end - start) + " ms"); } /** * Creates the server and starts a preview at the given port * * @param port the port number for the server to run on. */ public void preview(Integer port) { Server server = new Server(port); server.startPreview(); server.openBrowser(); } private void printHelpMessage() { String title = this.getClass().getPackage().getImplementationTitle(); String ver = this.getClass().getPackage().getImplementationVersion(); String author = this.getClass().getPackage().getImplementationVendor(); String header = title + " version " + ver + " copyright " + author; String desc = "a simple and fast static site generator"; String usage = "usage: " + title + " [subcommand] [options..] [arguments...]"; String moreHelp = "run " + title + " <subcommand> " + "--help to see more help about individual subcommands"; StringBuilder sb = new StringBuilder(); sb.append(header).append(LINE_SEPARATOR); if (this.version) { System.out.println(sb.toString()); } else { sb.append(desc) .append(LINE_SEPARATOR) .append(usage) .append(LINE_SEPARATOR) .append(moreHelp) .append(LINE_SEPARATOR + LINE_SEPARATOR); System.out.println(sb.toString()); } } private void initializeConfigurationSettings(Path path, String name) throws NumberFormatException, IOException { String nam, tag, auth, src, out, date;// = config.getSiteName();//, String port;// = config.getPort(); showWelcomeMessage(path, name); BufferedReader br = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)); System.out.println("1. What would you like to call your site?(" + config.getSiteName() + "):"); nam = br.readLine(); System.out.println("2. Who's authoring this site?"); auth = br.readLine(); System.out.println("3. What will be the tagline for the site?(" + config.getSiteTagline() + "):"); tag = br.readLine(); System.out.println("4. What will you like to name the folder where your posts will be stored?(" + config.getSourceDir() + "):"); src = br.readLine(); System.out.println("5. What will you like to name the folder where the generated site will be stored?(" + config.getOutputDir() + "):"); out = br.readLine(); System.out.println("6. What will you like to format the dates on yourData. posts and pages as?(" + config.getInputDateFormat() + "):"); date = br.readLine(); System.out.println("7. On what port will you like to see the live preview of your site?(" + config.getPort() + "):"); port = br.readLine(); finalizeConfigurationSettings(nam, auth, tag, src, out, date, port); } private void finalizeConfigurationSettings(String nam, String auth, String tag, String src, String out, String date, String port) { if (nam != null && !nam.equals(config.getSiteName()) && !nam.equals("")) { config.withSiteName(nam); } if (auth != null && !auth.equals(config.getSiteAuthor()) && !auth.equals("")) { config.withSiteAuthour(auth); } if (tag != null && !tag.equals(config.getSiteTagline()) && !tag.equals("")) { config.withSiteTagline(tag); } if (src != null && !src.equals(config.getSourceDir()) && !src.equals("")) { config.withSourceDir(src); } if (out != null && !out.equals(config.getOutputDir()) && !out.equals("")) { config.withOutputDir(out); } if (date != null && !date.equals(config.getInputDateFormat()) && !date.equals("")) { config.withDateFormat(date); } if (port != null && !port.equals(config.getPort().toString()) && !port.equals("")) { config.withPort(Integer.parseInt(port)); } } private void showWelcomeMessage(Path path, String name) { StringBuilder welcomeMessage = new StringBuilder(); printAsciiGriffin(); welcomeMessage.append("Heya! This is griffin. Your personal, fast and easy static site generator. (And an overall good guy)").append(LINE_SEPARATOR); welcomeMessage.append("You have chosen to create your new griffin site at: ").append(path.resolve(name).toString()).append(LINE_SEPARATOR); welcomeMessage.append("I'd love to help you set up some initial settings for your site, so let's go.").append(LINE_SEPARATOR); welcomeMessage.append("I'll ask you a set of simple questions and you can type in your answer. Some questions have a default answer, which will be marked in brackets.").append(LINE_SEPARATOR).append("You can just press enter to accept the default value in those cases.").append(LINE_SEPARATOR).append(LINE_SEPARATOR); System.out.println(welcomeMessage); } public void printAsciiGriffin() { System.out.println(" ___ ___ "); System.out.println(" __ /'___\\ /'___\\ __ "); System.out.println(" __ _ __ /\\_\\ /\\ \\__/ /\\ \\__/ /\\_\\ ___ "); System.out.println(" /'_ `\\ /\\`'__\\\\/\\ \\\\ \\ ,__\\\\ \\ ,__\\\\/\\ \\ /' _ `\\ "); System.out.println("/\\ \\L\\ \\\\ \\ \\/ \\ \\ \\\\ \\ \\_/ \\ \\ \\_/ \\ \\ \\ /\\ \\/\\ \\ "); System.out.println("\\ \\____ \\\\ \\_\\ \\ \\_\\\\ \\_\\ \\ \\_\\ \\ \\_\\\\ \\_\\ \\_\\"); System.out.println(" \\/___L\\ \\\\/_/ \\/_/ \\/_/ \\/_/ \\/_/ \\/_/\\/_/"); System.out.println(" /\\____/ "); System.out.println(" \\_/__/ "); } private void checkPathValidity(Path path, String name) throws FileSystemException, NotDirectoryException, FileAlreadyExistsException { if (!Files.isWritable(path)) { System.out.println("That path doesn't seem to be writable :(" + LINE_SEPARATOR + "Check if you have write permission to that path and try again."); throw new java.nio.file.FileSystemException(path.toString()); } if (Files.exists(path.resolve(name))) { System.out.println("Aw shucks! It seems like there is already a file of that name at that path :(" + LINE_SEPARATOR + "Try again with another name."); throw new FileAlreadyExistsException(path.resolve(name).toString()); } if (!Files.isDirectory(path)) { System.out.println("Aw, man. That path does not seem to be a valid directory :(" + LINE_SEPARATOR + "Try with another path again."); throw new java.nio.file.NotDirectoryException(path.toString()); } } /** * @param args the command line arguments * @throws java.io.IOException the exception * @throws java.lang.InterruptedException the exception */ public static void main(String[] args) throws IOException, InterruptedException { try { Griffin griffin = new Griffin(); CmdLineParser parser = new CmdLineParser(griffin, ParserProperties.defaults().withUsageWidth(120)); parser.parseArgument(args); if (griffin.help || griffin.version || args.length == 0) { griffin.printHelpMessage(); parser.printUsage(System.out); } else { griffin.commands.execute(); } } catch (CmdLineException ex) { Logger.getLogger(Griffin.class.getName()).log(Level.SEVERE, null, ex); } } }