Я попытался сделать что-то похожее с enum-картами и файлами свойств (см. Код ниже). но мои перечисления были простыми и имели только одно значение, за исключением встроенного регистра. у меня может быть что-то более безопасное. я поищу вокруг.
package p;
import java.util.*;
import java.io.*;
public class GenericAttributes<T extends Enum<T>> {
public GenericAttributes(final Class<T> keyType) {
map = new EnumMap<T, Object>(this.keyType = keyType);
}
public GenericAttributes(final Class<T> keyType, final Properties properties) {
this(keyType);
addStringProperties(properties);
}
public Object get(final T key) {
// what does a null value mean?
// depends on P's semantics
return map.containsKey(key) ? map.get(key) : null;
}
public boolean contains(final T key) {
return map.containsKey(key);
}
public void change(final T key, final Object value) {
remove(key);
put(key, value);
}
public Object put(final T key, final Object value) {
if (map.containsKey(key))
throw new RuntimeException("map already contains: " + key);
else
return map.put(key, value);
}
public Object remove(final T key) {
if (!map.containsKey(key))
throw new RuntimeException("map does not contain: " + key);
return map.remove(key);
}
public String toString() {
return toString(defaultEquals, defaultEndOfLine);
}
// maybe we don;t need this stuff
// we have tests for it though
// it might be useful
public String toString(final String equals, final String endOfLine) {
final StringBuffer sb = new StringBuffer();
for (Map.Entry<T, Object> entry : map.entrySet())
sb.append(entry.getKey()).append(equals).append(entry.getValue()).append(endOfLine);
return sb.toString();
}
public Properties toProperties() {
final Properties p = new Properties();
for (Map.Entry<T, Object> entry : map.entrySet())
p.put(entry.getKey().toString(), entry.getValue().toString());
return p;
}
public void addStringProperties(final Properties properties) {
// keep this for strings, but mostly do work in the enum class
// i.e. static GenericAttributes<PA> fromProperties();
// which would use a fromString()
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
final String key = (String) entry.getKey();
final String value = (String) entry.getValue();
addProperty(key, value);
}
}
public void addProperty(final String key, final Object value) {
try {
final T e = Enum.valueOf(keyType, key);
map.put(e, value);
} catch (IllegalArgumentException e) {
System.err.println(key + " is not an enum from: " + keyType);
}
}
public int size() {
return map.size();
}
public static Properties load(final InputStream inputStream,final Properties defaultProperties) {
final Properties p=defaultProperties!=null?new Properties(defaultProperties):new Properties();
try {
p.load(inputStream);
} catch(IOException e) {
throw new RuntimeException(e);
}
return p;
}
public static Properties load(final File file,final Properties defaultProperties) {
Properties p=null;
try {
final InputStream is=new FileInputStream(file);
p=load(is,defaultProperties);
is.close();
} catch(IOException e) {
throw new RuntimeException(e);
}
return p;
}
public static void store(final OutputStream outputStream, final Properties properties) {
try {
properties.store(outputStream, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void store(final File file, final Properties properties) {
try {
final OutputStream os = new FileOutputStream(file);
store(os, properties);
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
final Class<T> keyType;
static final String defaultEquals = "=", defaultEndOfLine = "\n";
private final EnumMap<T, Object> map;
public static void main(String[] args) {
}
}
package p;
import static org.junit.Assert.*;
import org.junit.*;
import java.io.*;
import java.util.*;
enum A1 {
foo,bar,baz;
}
enum A2 {
x,y,z;
}
public class GenericAttributesTestCase {
@Test public void testGenericAttributes() {
new GenericAttributes<A1>(A1.class);
}
@Test public void testGenericAttributesKeyTypeProperties() {
final Properties expected=gA1.toProperties();
final GenericAttributes<A1> gA=new GenericAttributes<A1>(A1.class,expected);
final Properties actual=gA.toProperties();
assertEquals(expected,actual);
}
@Test public void testGet() {
final A1 key=A1.foo;
emptyGA1.put(key,null);
final Object actual=emptyGA1.get(key);
assertEquals(null,actual);
}
@Test public void testGetInteger() {
// attributes.add(key,integer);
// assertEquals(integer,attributes.get("key"));
}
@Test public void testContains() {
for(A1 a:A1.values())
assertFalse(emptyGA1.contains(a));
}
@Test public void testChange() {
final A1 key=A1.foo;
final Integer value=42;
emptyGA1.put(key,value);
final Integer expected=43;
emptyGA1.change(key,expected);
final Object actual=emptyGA1.get(key);
assertEquals(expected,actual);
}
@Test public void testAdd() {
final A1 key=A1.foo;
final Integer expected=42;
emptyGA1.put(key,expected);
final Object actual=emptyGA1.get(key);
assertEquals(expected,actual);
}
@Test public void testRemove() {
final A1 key=A1.foo;
final Integer value=42;
emptyGA1.put(key,value);
emptyGA1.remove(key);
assertFalse(emptyGA1.contains(key));
}
@Test public void testToString() {
final String actual=gA1.toString();
final String expected="foo=a foo value\nbar=a bar value\n";
assertEquals(expected,actual);
}
@Test public void testToStringEqualsEndOfLine() {
final String equals=",";
final String endOFLine=";";
final String actual=gA1.toString(equals,endOFLine);
final String expected="foo,a foo value;bar,a bar value;";
assertEquals(expected,actual);
}
@Test public void testEmbedded() {
final String equals=",";
final String endOfLine=";";
//System.out.println("toString(\""+equals+"\",\""+endOFLine+"\"):");
final String embedded=gA1.toString(equals,endOfLine);
GenericAttributes<A2> gA2=new GenericAttributes<A2>(A2.class);
gA2.put(A2.x,embedded);
//System.out.println("embedded:\n"+gA2);
// maybe do file={name=a.jpg;dx=1;zoom=.5}??
// no good, key must be used more than once
// so file:a.jpg={} and hack
// maybe file={name=...} will work
// since we have to treat it specially anyway?
// maybe this is better done in ss first
// to see how it grows?
}
@Test public void testFromString() {
// final Attributes a=Attributes.fromString("");
// final String expected="";
// assertEquals(expected,a.toString());
}
@Test public void testToProperties() {
final Properties expected=new Properties();
expected.setProperty("foo","a foo value");
expected.setProperty("bar","a bar value");
final Properties actual=gA1.toProperties();
assertEquals(expected,actual);
}
@Test public void testAddProperties() {
final Properties p=gA1.toProperties();
final GenericAttributes<A1> ga=new GenericAttributes<A1>(A1.class);
ga.addStringProperties(p);
// assertEquals(ga1,ga); // fails since we need to define equals!
// hack, go backwards
final Properties p2=ga.toProperties();
assertEquals(p,p2); // hack until we define equals
}
@Test public void testStore() throws Exception {
final Properties expected=gA1.toProperties();
final ByteArrayOutputStream baos=new ByteArrayOutputStream();
GenericAttributes.store(baos,expected);
baos.close();
final byte[] bytes=baos.toByteArray();
final ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
final Properties actual=GenericAttributes.load(bais,null);
bais.close();
assertEquals(expected,actual);
}
@Test public void testLoad() throws Exception {
final Properties expected=gA1.toProperties();
final ByteArrayOutputStream baos=new ByteArrayOutputStream();
GenericAttributes.store(baos,expected);
baos.close();
final ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
final Properties actual=GenericAttributes.load(bais,null);
bais.close();
assertEquals(expected,actual);
}
@Test public void testMain() {
// fail("Not yet implemented");
}
GenericAttributes<A1> gA1=new GenericAttributes<A1>(A1.class);
{
gA1.put(A1.foo,"a foo value");
gA1.put(A1.bar,"a bar value");
}
GenericAttributes<A1> emptyGA1=new GenericAttributes<A1>(A1.class);
}
отвечая на ваш комментарий:
похоже, что я получаю значения, используя enum в качестве ключа. Я, вероятно, смущен.
перечисление может реализовать интерфейс, и каждый набор перечислений может иметь экземпляр этого базового класса и делегировать вызовы к нему (см. Пункт 34 http://java.sun.com/docs/books/effective/toc.html)
Я нашел другой код, который шел с моими общими атрибутами (см. Ниже), но я не могу найти никаких тестов для него, и я не совсем уверен, что я делал, кроме, возможно, добавления более сильной типизации. *
Моей мотивацией для всего этого было сохранение некоторых атрибутов для средства просмотра фотографий, таких как Picasa, я хотел сохранить набор атрибутов для изображения в одной строке файла свойств
package p;
import java.util.*;
public enum GA {
// like properties, seems like this wants to be constructed with a set of default values
i(Integer.class) {
Integer fromString(final String s) {
return new Integer(s);
}
Integer fromNull() {
return zero; // return empty string?
}
},
b(Boolean.class) {
Boolean fromString(final String s) {
return s.startsWith("t")?true:false;
}
Boolean fromNull() {
return false;
}
},
d(Double.class) {
Double fromString(final String s) {
return new Double(s);
}
Double fromNull() {
return new Double(zero);
}
};
GA() {
this(String.class);
}
GA(final Class clazz) {
this.clazz=clazz;
}
abstract Object fromString(String string);
abstract Object fromNull();
static GenericAttributes<GA> fromProperties(final Properties properties) {
final GenericAttributes<GA> pas=new GenericAttributes<GA>(GA.class);
for(Map.Entry<Object,Object> entry:properties.entrySet()) {
final String key=(String)entry.getKey();
final GA pa=valueOf(key);
if(pa!=null) {
final String stringValue=(String)entry.getValue();
Object value=pa.fromString(stringValue);
pas.addProperty(key,value);
} else throw new RuntimeException(key+"is not a member of "+"GA");
}
return pas;
}
// private final Object defaultValue; // lose type?; require cast?
/* private */final Class clazz;
static final Integer zero=new Integer(0);
}