package net.ocheyedan.wrk.cmd.trello; import net.ocheyedan.wrk.Json; import net.ocheyedan.wrk.Output; import net.ocheyedan.wrk.cmd.Args; import net.ocheyedan.wrk.cmd.Command; import net.ocheyedan.wrk.cmd.Usage; import org.codehaus.jackson.type.TypeReference; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URLEncoder; import java.util.*; /** * User: blangel * Date: 6/30/12 * Time: 3:03 PM * * Collects wrk ids and saves them for subsequent use by other commands. */ abstract class IdCommand extends Command { static final class TrelloId { final String id; final String idWithTypePrefix; TrelloId(String id, String idWithTypePrefix) { this.id = id; this.idWithTypePrefix = idWithTypePrefix; } } @SuppressWarnings("serial") protected static final Set<String> orgPrefix = new HashSet<String>(1) { { add("o:"); } }; @SuppressWarnings("serial") protected static final Set<String> boardsPrefix = new HashSet<String>(1) { { add("b:"); } }; @SuppressWarnings("serial") protected static final Set<String> listsPrefix = new HashSet<String>(1) { { add("l:"); } }; @SuppressWarnings("serial") protected static final Set<String> cardsPrefix = new HashSet<String>(1) { { add("c:"); } }; @SuppressWarnings("serial") protected static final Set<String> membersPrefix = new HashSet<String>(1) { { add("m:"); } }; @SuppressWarnings("serial") protected static final Set<String> boardsListsPrefix = new HashSet<String>(1) { { add("b:"); add("l:"); } }; @SuppressWarnings("serial") protected static final Set<String> boardsListsCardsPrefix = new HashSet<String>(1) { { add("b:"); add("l:"); add("c:"); } }; @SuppressWarnings("serial") protected static final Set<String> orgsBoardsCardsPrefix = new HashSet<String>(1) { { add("o:"); add("b:"); add("c:"); } }; @SuppressWarnings("serial") protected static final Set<String> allPrefix = new HashSet<String>(1) { { add("o:"); add("b:"); add("l:"); add("c:"); add("m:"); } }; private final File wrkIdsFile; private final LinkedList<Map<String, String>> existingWrkIds; private final Map<String, String> existingHead; protected IdCommand(Args args) { super(args); LinkedList<Map<String, String>> existing = new LinkedList<Map<String, String>>(); wrkIdsFile = new File(String.format("%s%s%s%s%s", System.getProperty("user.home"), File.separator, ".wrk", File.separator, "wrk-ids")); try { if (wrkIdsFile.exists()) { existing = Json.mapper().readValue(wrkIdsFile, new TypeReference<LinkedList<Map<String, String>>>() { }); } } catch (FileNotFoundException fnfe) { // ignore } catch (IOException ioe) { Output.print(ioe); } this.existingWrkIds = existing; Map<String, String> existingHead = existingWrkIds.peek(); existingHead = (existingHead == null ? Collections.<String, String>emptyMap() : existingHead); this.existingHead = existingHead; } protected abstract boolean valid(); protected abstract String getCommandName(); @Override public final void run() { if (!valid()) { Output.print("^red^Invalid arguments to command ^i^%s^r^^red^: %s^r^", getCommandName(), args); new Usage(getCommandName()).run(); return; } Map<String, String> wrkids = _run(); if (wrkids.isEmpty()) { return; // don't push an empty } try { if (existingWrkIds.size() >= 50) { existingWrkIds.removeLast(); } existingWrkIds.push(wrkids); Json.mapper().writeValue(wrkIdsFile, existingWrkIds); } catch (IOException ioe) { Output.print(ioe); } } protected void pop(int times) { for (int i = 0; i < times; i++) { if (!this.existingWrkIds.isEmpty()) { this.existingWrkIds.pop(); } else { break; } } try { Json.mapper().writeValue(wrkIdsFile, existingWrkIds); } catch (IOException ioe) { Output.print(ioe); } } static String validate(String value, String type, String plural) { return validate(value, type, plural, false); } static String validate(String value, String type, String plural, boolean failOnLength) { if ((value == null) || value.isEmpty()) { Output.print("^red^%s was empty, doing nothing.^r^", type); System.exit(0); } if (value.length() > 16384) { if (!failOnLength) { Output.print("^red^Trello %s must be less than 16,384 characters, shortening.^r^", plural); return value.substring(0, 16384); } else { Output.print("^red^Trello %s must be less than 16,384 characters, doing nothing.^r^", plural); System.exit(0); } } return value; } static String encode(String comment) { try { return URLEncoder.encode(comment, "UTF-8"); } catch (IOException ioe) { Output.print(ioe); } return comment; } protected TrelloId parseWrkId(String wrkId, Set<String> desiredPrefixes) { String trelloId = existingHead.get(wrkId); if (trelloId == null) { return new TrelloId(wrkId, wrkId); // user entered a trello-id directly } String existingPrefix = (trelloId.length() > 2 ? trelloId.substring(0, 2) : ""); if (!desiredPrefixes.contains(existingPrefix)) { // given a wrk-id for the wrong prefix; alert and exit. String type = prettyPrint(existingPrefix); Output.print("The wrk-id [ ^b^%s^r^ ] is for ^red^%s^r^ but the command is for %s.", wrkId, type, prefixesToString(desiredPrefixes)); System.exit(1); } return new TrelloId(trelloId.substring(2), trelloId); } private String prettyPrint(String prefix) { if ("b:".equals(prefix)) { return "boards"; } else if ("c:".equals(prefix)) { return "cards"; } else if ("o:".equals(prefix)) { return "orgs"; } else if ("m:".equals(prefix)) { return "members"; } else if ("l:".equals(prefix)) { return "lists"; } else { return "<unknown>"; } } private String prefixesToString(Set<String> prefixes) { StringBuilder buffer = new StringBuilder("[ "); boolean first = true; for (String prefix : prefixes) { if (!first) { buffer.append(", "); } buffer.append("^b^"); buffer.append(prettyPrint(prefix)); buffer.append("^r^"); first = false; } buffer.append(" ]"); return buffer.toString(); } protected abstract Map<String, String> _run(); }