package org.netmelody.cieye.server.observation; import java.lang.reflect.Type; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.netmelody.cieye.core.observation.CodeBook; import org.netmelody.cieye.core.observation.CommunicationNetwork; import org.netmelody.cieye.core.observation.Contact; import org.netmelody.cieye.server.CiEyeNewVersionChecker; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import static com.google.common.collect.Iterables.transform; import static java.lang.Integer.parseInt; public final class GovernmentWatchdog implements CiEyeNewVersionChecker { private final Contact contact; public static final class TagsHolder { private Tags tags; public TagsHolder() { this(ImmutableList.<Tag>of()); } public TagsHolder(Iterable<Tag> tagNames) { this.tags = new Tags(tagNames); } public String latest() { return tags.latest(); } } public static final class Tags { private final ArrayList<Tag> names; public Tags(Iterable<Tag> tagNames) { this.names = Lists.newArrayList(tagNames); Collections.sort(names, Tag.COMPARATOR); } public String latest() { if (names.isEmpty()) { return ""; } return names.get(names.size() - 1).name; } } public static final class Tag { private static final Pattern REGEX = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)([0-9A-Za-z-]*)"); private static final Comparator<Tag> COMPARATOR = new Comparator<Tag>() { @Override public int compare(Tag o1, Tag o2) { for (int i = 0; i < 3; i++) { if (o1.version[i] != o2.version[i]) { return o1.version[i] > o2.version[i] ? 1 : -1; } } return o1.specialSuffix.compareTo(o2.specialSuffix); } }; private final String name; private final int[] version; private final String specialSuffix; public Tag(String name) { this.name = (name == null) ? "" : name; final Matcher matcher = REGEX.matcher(this.name); matcher.matches(); version = new int[] {parseInt(matcher.group(1)), parseInt(matcher.group(2)), parseInt(matcher.group(3))}; specialSuffix = "".equals(matcher.group(4)) ? "~" : matcher.group(4); } } public static final class TagsHolderAdapter implements JsonDeserializer<TagsHolder> { @Override public TagsHolder deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { return new TagsHolder(transform(json.getAsJsonArray(), toTagNames())); } private Function<JsonElement, Tag> toTagNames() { return new Function<JsonElement, Tag>() { @Override public Tag apply(JsonElement entry) { return new Tag(entry.getAsJsonObject().get("name").getAsString()); } }; } } public GovernmentWatchdog(CommunicationNetwork network) { contact = network.makeContact(new CodeBook().withJsonDeserializerFor(TagsHolder.class, new TagsHolderAdapter())); } @Override public String getLatestVersion() { try { InetAddress.getAllByName("api.github.com"); } catch (UnknownHostException firewalled) { return ""; } final TagsHolder tags = contact.makeJsonRestCall("https://api.github.com/repos/netmelody/ci-eye/tags", TagsHolder.class); return tags.latest(); } }