/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.importer; import java.net.URI; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.voltcore.utils.Punycode; import com.google_voltpatches.common.base.Function; import com.google_voltpatches.common.base.Joiner; import com.google_voltpatches.common.base.Preconditions; import com.google_voltpatches.common.base.Predicate; public class ChannelSpec implements Comparable<ChannelSpec> { static final Pattern specRE = Pattern .compile("(?<importer>(?:[\\w_-]+\\.)*[\\w_-]+)\\|(?<encodeduri>(?:[\\w-_\\.]+))"); static Joiner nodeJoiner = Joiner.on('/').skipNulls(); final String importer; final URI uri; public ChannelSpec(String importer, URI uri) { this.uri = Preconditions.checkNotNull(uri, "channel URI is null"); Preconditions.checkArgument( importer != null && !importer.trim().isEmpty(), "importer is null or empty" ); this.importer = importer; } public ChannelSpec(String importer, String uri) { this(importer, URI.create(uri)); } public ChannelSpec(String jsonValue) { Preconditions.checkArgument( jsonValue != null && !jsonValue.trim().isEmpty(), "node is null or empty" ); Matcher mtc = specRE.matcher(jsonValue); Preconditions.checkArgument( mtc.matches(), "Invalid encoded JSON value specification for %s", jsonValue ); String strUri = Punycode.decode(mtc.group("encodeduri")); this.uri = URI.create(strUri); this.importer = mtc.group("importer"); } public String asJSONValue() { StringBuilder sb = new StringBuilder(128); sb.append(importer).append('|'); sb.append(Punycode.encode(uri.toString())); return sb.toString(); } public String getImporter() { return importer; } public URI getUri() { return uri; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((importer == null) ? 0 : importer.hashCode()); result = prime * result + ((uri == null) ? 0 : uri.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ChannelSpec other = (ChannelSpec) obj; if (importer == null) { if (other.importer != null) return false; } else if (!importer.equals(other.importer)) return false; if (uri == null) { if (other.uri != null) return false; } else if (!uri.equals(other.uri)) return false; return true; } @Override public String toString() { return "ChannelSpec [importer=" + importer + ", uri=" + uri + "]"; } @Override public int compareTo(ChannelSpec o) { int cmp = importer.compareTo(o.importer); if (cmp == 0) { cmp = uri.compareTo(o.uri); } return cmp; } public final static Predicate<ChannelSpec> importerIs(final String importer) { return new Predicate<ChannelSpec>() { @Override final public boolean apply(ChannelSpec spec) { return importer.equals(spec.importer); } }; } public static <V> Predicate<Map.Entry<ChannelSpec, V>> importerKeyIs(final String importer, Class<V> clazz) { return new Predicate<Map.Entry<ChannelSpec, V>> () { @Override public boolean apply(Entry<ChannelSpec, V> e) { return importer.equals(e.getKey().getImporter()); } }; } public static <V> Predicate<Map.Entry<ChannelSpec, V>> specKeyIs(final ChannelSpec spec, Class<V> clazz) { return new Predicate<Map.Entry<ChannelSpec, V>> () { @Override public boolean apply(Entry<ChannelSpec, V> e) { return spec.equals(e.getKey()); } }; } public static <V> Predicate<Map.Entry<ChannelSpec, V>> specKeyIn(final Set<ChannelSpec> specs, Class<V> clazz) { return new Predicate<Map.Entry<ChannelSpec, V>> () { @Override public boolean apply(Entry<ChannelSpec, V> e) { return specs.contains(e.getKey()); } @Override public String toString() { return "Predicate.specKeyIn [Map.Entry.getKey() is contained in " + specs + " ]"; } }; } public static <K> Predicate<Map.Entry<K, ChannelSpec>> importerValueIs(final String importer, Class<K> clazz) { return new Predicate<Map.Entry<K, ChannelSpec>> () { @Override public boolean apply(Entry<K, ChannelSpec> e) { return importer.equals(e.getValue().getImporter()); } }; } public static <K> Predicate<Map.Entry<K, ChannelSpec>> specValueIs(final ChannelSpec spec, Class<K> clazz) { return new Predicate<Map.Entry<K, ChannelSpec>> () { @Override public boolean apply(Entry<K, ChannelSpec> input) { return spec.equals(input.getValue()); } }; } public final static Function<String, ChannelSpec> asSelf = new Function<String, ChannelSpec>() { @Override final public ChannelSpec apply(String input) { return new ChannelSpec(input); } }; public final static Function<URI,ChannelSpec> fromUri(final String importer) { return new Function<URI,ChannelSpec>() { @Override public ChannelSpec apply(URI uri) { return new ChannelSpec(importer,uri); } }; } }