package nodebox.node;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class Device {
public static final String TYPE_OSC = "osc";
public static final String TYPE_AUDIOPLAYER = "audioplayer";
public static final String TYPE_AUDIOINPUT = "audioinput";
public static final String TIMELINE_SYNC = "sync_with_timeline";
public static final ImmutableList<String> deviceTypes = ImmutableList.of(TYPE_OSC, TYPE_AUDIOPLAYER, TYPE_AUDIOINPUT);
private final String name;
private final String type;
private final ImmutableMap<String, String> properties;
private static final Pattern OSC_PROPERTY_NAMES_PATTERN = Pattern.compile("^(port|sync_with_timeline)$");
private static final Pattern AUDIOPLAYER_PROPERTY_NAMES_PATTERN = Pattern.compile("^(filename|sync_with_timeline|loop)$");
private static final Pattern AUDIOINPUT_PROPERTY_NAMES_PATTERN = Pattern.compile("^(sync_with_timeline)$");
private final transient int hashCode;
private static final Map<String, Pattern> validPropertyNames;
static {
ImmutableMap.Builder<String, Pattern> builder = new ImmutableMap.Builder<String, Pattern>();
builder.put(TYPE_OSC, OSC_PROPERTY_NAMES_PATTERN);
builder.put(TYPE_AUDIOPLAYER, AUDIOPLAYER_PROPERTY_NAMES_PATTERN);
builder.put(TYPE_AUDIOINPUT, AUDIOINPUT_PROPERTY_NAMES_PATTERN);
validPropertyNames = builder.build();
}
public static Device oscDevice(String name, long port, boolean syncWithTimeline) {
return new Device(name, TYPE_OSC, ImmutableMap.<String, String>of("port", String.valueOf(port), TIMELINE_SYNC, String.valueOf(syncWithTimeline)));
}
public static Device deviceForType(String name, String type) {
checkNotNull(type, "Type cannot be null.");
checkArgument(deviceTypes.contains(type), "%s is not a valid device type.", type);
// If the type is not found in the default values, get() returns null, which is what we need for custom types.
return new Device(name, type, ImmutableMap.<String, String>of());
}
private Device(final String name, final String type, final ImmutableMap<String, String> properties) {
this.name = name;
this.type = type;
this.properties = properties;
this.hashCode = Objects.hashCode(name, type, properties);
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public boolean hasProperty(String name) {
return properties.containsKey(name);
}
public String getProperty(String name) {
return properties.get(name);
}
public Map<String, String> getProperties() {
return properties;
}
public String getProperty(String name, String defaultValue) {
if (hasProperty(name)) {
return properties.get(name);
} else {
return defaultValue;
}
}
public Device withProperty(String name, String value) {
checkArgument(isValidProperty(name), "Property name '%s' is not valid.", name);
Map<String, String> b = new HashMap<String, String>();
b.putAll(properties);
b.put(name, value);
return new Device(this.name, this.type, ImmutableMap.copyOf(b));
}
private boolean isValidProperty(String name) {
checkNotNull(name);
if (!validPropertyNames.containsKey(getType())) return false;
return validPropertyNames.get(getType()).matcher(name).matches();
}
//// Object overrides ////
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Device)) return false;
final Device other = (Device) o;
return Objects.equal(name, other.name)
&& Objects.equal(type, other.type)
&& Objects.equal(properties, other.properties);
}
@Override
public String toString() {
return String.format("<Device %s (%s)>", name, type);
}
}