
org.monte.media.Format Maven / Gradle / Ivy
/*
* @(#)Format.java
*
* Copyright (c) 2011-2012 Werner Randelshofer, Goldau, Switzerland.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance onlyWith the
* license agreement you entered into onlyWith Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.monte.media;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Specifies the format of a media, for example of audio and video.
*
* @author Werner Randelshofer
* @version $Id: Format.java 299 2013-01-03 07:40:18Z werner $
*/
public class Format {
/**
* Holds the properties of the format.
*/
private HashMap properties;
/**
* Creates a new format onlyWith the specified properties.
*/
public Format(Map properties) {
this(properties, true);
}
/**
* Creates a new format onlyWith the specified properties.
*/
private Format(Map properties, boolean copy) {
if (copy || ! (properties instanceof HashMap)) {
for (Map.Entry e : properties.entrySet()) {
if (!e.getKey().isAssignable(e.getValue())) {
throw new ClassCastException(e.getValue() + " must be of type " + e.getKey().getValueClass());
}
}
this.properties = new HashMap< FormatKey, Object>(properties);
} else {
this.properties = (HashMap< FormatKey, Object>) properties;
}
}
/**
* Creates a new format onlyWith the specified properties. The properties
* must be given as key value pairs.
*/
public Format(Object... p) {
this.properties = new HashMap< FormatKey, Object>();
for (int i = 0; i < p.length; i += 2) {
FormatKey key = (FormatKey) p[i];
if (!key.isAssignable(p[i + 1])) {
throw new ClassCastException(key + ": " + p[i + 1] + " must be of type " + key.getValueClass());
}
this.properties.put(key, p[i + 1]);
}
}
@SuppressWarnings("unchecked")
public T get(FormatKey key) {
return (T) properties.get(key);
}
@SuppressWarnings("unchecked")
public T get(FormatKey key, T defaultValue) {
return (properties.containsKey(key)) ? (T) properties.get(key) : defaultValue;
}
public boolean containsKey(FormatKey key) {
return properties.containsKey(key);
}
/**
* Gets the properties of the format as an unmodifiable map.
*/
public Map getProperties() {
return Collections.unmodifiableMap(properties);
}
/**
* Gets the keys of the format as an unmodifiable set.
*/
public Set getKeys() {
return Collections.unmodifiableSet(properties.keySet());
}
/**
* Returns true if that format matches this format. That is iff all
* properties defined in both format objects are identical. Properties which
* are only defined in one of the format objects are not considered.
*
* @param that Another format.
* @return True if the other format matches this format.
*/
public boolean matches(Format that) {
for (Map.Entry e : properties.entrySet()) {
if (!e.getKey().isComment()) {
if (that.properties.containsKey(e.getKey())) {
Object a = e.getValue();
Object b = that.properties.get(e.getKey());
if (a != b && a == null || !a.equals(b)) {
return false;
}
}
}
}
return true;
}
public boolean matchesWithout(Format that, FormatKey... without) {
OuterLoop:
for (Map.Entry e : properties.entrySet()) {
FormatKey k = e.getKey();
if (!e.getKey().isComment()) {
if (that.properties.containsKey(k)) {
for (int i = 0; i < without.length; i++) {
if (without[i] == k) {
continue OuterLoop;
}
}
Object a = e.getValue();
Object b = that.properties.get(k);
if (a != b && a == null || !a.equals(b)) {
return false;
}
}
}
}
return true;
}
/**
* Creates a new format which contains all properties from this format and
* additional properties from that format. If a property is specified in
* both formats, then the property value from this format is used. It
* overwrites that format.
If one of the format has more properties than
* the other, then the new format is more specific than this format.
*
* @param that
* @return That format with properties overwritten by this format.
*/
public Format append(Format that) {
HashMap m = new HashMap(this.properties);
for (Map.Entry e : that.properties.entrySet()) {
if (!m.containsKey(e.getKey())) {
m.put(e.getKey(), e.getValue());
}
}
return new Format(m,false);
}
/**
* Creates a new format which contains all properties from this format and
* additional properties listed. If a property is specified in both
* formats, then the property value from this format is used. It overwrites
* that format.
If one of the format has more properties than the other,
* then the new format is more specific than this format.
*
* @param p The properties must be given as key value pairs.
* @return That format with properties overwritten by this format.
*/
public Format append(Object... p) {
HashMap m = new HashMap(this.properties);
for (int i = 0; i < p.length; i += 2) {
FormatKey key = (FormatKey) p[i];
if (!key.isAssignable(p[i + 1])) {
throw new ClassCastException(key + ": " + p[i + 1] + " must be of type " + key.getValueClass());
}
m.put(key, p[i + 1]);
}
return new Format(m,false);
}
/**
* Creates a new format which contains all properties from the specified
* format and additional properties from this format.
* If a property is specified in both formats, then the property value
* from this format is used. It overwrites that format.
*
If one of the format has more properties than the other, then the new
* format is more specific than this format.
*
* @param that
* @return That format with properties overwritten by this format.
*/
public Format prepend(Format that) {
HashMap m = new HashMap(that.properties);
for (Map.Entry e : this.properties.entrySet()) {
if (!m.containsKey(e.getKey())) {
m.put(e.getKey(), e.getValue());
}
}
return new Format(m,false);
}
/**
* Creates a new format which contains all specified properties and
* additional properties from this format.
* If a property is specified in both formats, then the property value
* from this format is used. It overwrites that format.
*
If one of the format has more properties than the other, then the new
* format is more specific than this format.
*
* @param p The properties must be given as key value pairs.
* @return That format with properties overwritten by this format.
*/
public Format prepend(Object... p) {
HashMap m = new HashMap();
for (int i = 0; i < p.length; i += 2) {
FormatKey key = (FormatKey) p[i];
if (!key.isAssignable(p[i + 1])) {
throw new ClassCastException(key + ": " + p[i + 1] + " must be of type " + key.getValueClass());
}
m.put(key, p[i + 1]);
}
for (Map.Entry e : this.properties.entrySet()) {
if (!m.containsKey(e.getKey())) {
m.put(e.getKey(), e.getValue());
}
}
return new Format(m,false);
}
/**
* Creates a new format which only has the specified keys (or less). If
* the keys are reduced, then the new format is less specific than this
* format.
*/
public Format intersectKeys(FormatKey... keys) {
HashMap m = new HashMap();
for (FormatKey k : keys) {
if (properties.containsKey(k)) {
m.put(k, properties.get(k));
}
}
return new Format(m,false);
}
/**
* Creates a new format without the specified keys. If the keys are
* reduced, then the new format is less specific than this format.
*/
public Format removeKeys(FormatKey... keys) {
boolean needsRemoval = false;
for (FormatKey k : keys) {
if (properties.containsKey(k)) {
needsRemoval = true;
break;
}
}
if (!needsRemoval) {
return this;
}
HashMap m = new HashMap(properties);
for (FormatKey k : keys) {
m.remove(k);
}
return new Format(m,false);
}
/**
* Returns true if the format has the specified keys.
*/
public Format containsKeys(FormatKey... keys) {
HashMap m = new HashMap(properties);
for (FormatKey k : keys) {
m.remove(k);
}
return new Format(m,false);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder("Format{");
boolean isFirst = true;
for (Map.Entry e : properties.entrySet()) {
if (isFirst) {
isFirst = false;
} else {
buf.append(',');
}
buf.append(e.getKey().toString());
buf.append(':');
appendStuffedString(e.getValue(), buf);
}
buf.append('}');
return buf.toString();
}
/**
* This method is used by #toString.
*/
private static void appendStuffedString(Object value, StringBuilder stuffed) {
if (value == null) {
stuffed.append("null");
}
value = value.toString();
if (value instanceof String) {
for (char ch : ((String) value).toCharArray()) {
if (ch >= ' ') {
stuffed.append(ch);
} else {
String hex = Integer.toHexString(ch);
stuffed.append("\\u");
for (int i = hex.length(); i < 4; i++) {
stuffed.append('0');
}
stuffed.append(hex);
}
}
}
}
}