package com.breakersoft.plow; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; public final class FrameSet implements Iterable<Integer> { private static final List<Pattern> PATTERNS = ImmutableList.of( Pattern.compile("^(-?[0-9]+)-(-?[0-9]+)$"), Pattern.compile("^(-?[0-9]+)$"), Pattern.compile("^(-?[0-9]+)-(-?[0-9]+)([xy:]{1})([0-9]+)$")); private final Set<Integer> set; private final List<Integer> list; private final String range; public FrameSet(String range) { this.range = range; this.set = Sets.newHashSet(); this.list = Lists.newArrayList(); parseFrameRange(); } @Override public Iterator<Integer> iterator() { return list.iterator(); } public int indexOf(int frame) { return list.indexOf(frame); } public int size() { return list.size(); } public int get(int index) { return list.get(index); } public int last() { return list.get(list.size() - 1); } public int first() { return list.get(0); } public String toString() { return range; } public boolean contains(int frame) { return set.contains(frame); } private void parseFrameRange() { for (String part: range.split(",")) { for (int idx = 0; idx<PATTERNS.size(); idx++) { Matcher matcher = PATTERNS.get(idx).matcher(part); if (!matcher.matches()) { continue; } int start; int end; int chunk; switch (idx) { case 0: start = Integer.parseInt(matcher.group(1)); end = Integer.parseInt(matcher.group(2)); addFrames(range(start, end+1, 1)); break; case 1: start = Integer.parseInt(matcher.group(1)); addFrame(start); break; case 2: start = Integer.parseInt(matcher.group(1)); end = Integer.parseInt(matcher.group(2)); chunk = Integer.parseInt(matcher.group(4)); String mod = matcher.group(3); if (mod.equals("x")) { addFrames(range(start, end+1, chunk)); } else if(mod.equals("y")) { final Set<Integer> bad = ImmutableSet.copyOf( range(start, end+1, chunk)); for (int i: range(start, end+1, 1)) { if (!bad.contains(i)) { addFrame(i); } } } else if (mod.equals(":")) { for (int stagger: range(chunk, 0, -1)) { addFrames(range(start, end+1, stagger)); } } break; } } } if (list.size() == 0) { throw new IllegalArgumentException("Invalid/unparsable frame range:" + range); } } private void addFrame(int i) { if (!set.contains(i)) { list.add(i); set.add(i); } } private void addFrames(List<Integer> frames) { for (int i: frames) { if (!set.contains(i)) { list.add(i); set.add(i); } } } private static final List<Integer> range(int start, int end, int chunk) { if (chunk == 0) { throw new IllegalArgumentException("Chunk size cannot be 0."); } final int alloc = Math.abs((end - start) / chunk); final List<Integer> result = Lists.newArrayListWithExpectedSize(alloc); if (chunk < 0) { for (int i=start; i>end; i=i+chunk) { result.add(i); } } else if (chunk > 0) { for (int i=start; i<end; i=i+chunk) { result.add(i); } } return result; } }