From: Brian de A. <bs...@cs...> - 2007-08-30 22:21:59
|
Joshua, I'm not suggesting that the user's wouldn't have to do anything, nor that Address objects be automagically spoofed up by the JUNG code. Currently for load, they have to provide implementations of three Factories to create their model objects, and also write code to process the {vertex, edge, graph} attribute maps. For example, I think this is how Anne-Marie would *currently* do to load a graph (I'm assuming that Anne-Marie is creating some kind of CRM): GraphMLFileHandler handler = new GraphMLFileHandler(new CustomerFactory(), new RelationshipFactory(), new GraphFactory()); GraphMLFile reader = new GraphMLFile(handler); Graph<V,E> myGraph = reader.load("filename"); for(V vertex : myGraph.getVerticies()) { mySetVertexAttributes(v, handler.getVertexAttributes().get(v)); } for(E edge : myGraph.getEdges()) { mySetEdgeAttributes(e, handler.getEdgeAttributes().get(e)); } // implemented somewhere else ... class CustomerFactory implements Factory<Customer> { public Customer create() { return new Customer(); } } // or maybe this would be on Customer public void mySetVertexAttributes(Customer v, Map<String,String> attributeMap) { result.setName(attributeMap.get(CUSTOMER_NAME_KEY)); result.setAddress(Address.parse(attributeMap.get(ADDRESS_KEY))); } > The way that I've been thinking about doing this > looks something like this: > > setAttributeParser(String attribute, Transformer<String, ?> parser) > > which would add (attribute, parser) to an internal Map that would be > queried by the edge creation code for each remaining element in the > attributeMap; non-null parsers would then be invoked (and null parsers > would indicate that you just want the string->string mapping). That would require 1 transformer per attribute, plus the factory for creating. That's a fair bit more code overhead. And it also means JUNG now needs to know about attributes and their parser. What I'm suggesting is more a marshalling framework: GraphMLFileReader reader = new GraphMLReader(new MyVertexUnmarshaller(), new MyEdgeUnmarshaller(), new MyGraphUnmarshaller()); Graph<Customer,Relations> myGraph = reader.load("filename"); where the unmarshallers are something like: class MyVertexUnmarshaller<Map<String,String>,Customer> { public Customer transform(Map<String,String> attributeMap) { Customer result = new Customer(); result.setName(attributeMap.get(CUSTOMER_NAME_KEY)); result.setAddress(Address.parse(attributeMap.get(ADDRESS_KEY))); } } // similar for MyEdgeCreator and MyGraphCreator So the Factory and mySet*Attributes have been centralized into a set of transformers. Other graph-format writers could use the same interface. The inverse transforms would look something like: GraphMLWriter writer = new GraphMLWriter(new MyVertexMarshaller(), new MyEdgeMarshaller(), new MyGraphMarshaller()); reader.save(myGraph, "filename"); class MyVertexMarshaller<Customer, Map<String,String>> { public Map<String,String> transform(Customer c) { Map<String,String> map = new HashMap<String,String(); map.put(CUSTOMER_NAME_KEY, c.getName()); map.put(ADDRESS_KEY, Address.flatten(c.getAddress())); return map; } } // similar for MyEdgeMarshaller and MyGraphMarshaller And then yes, we can get rid of get{Vertex,Edge,Graph}Attributes(), and in fact make GraphMLFileHandler an inner class . And typing that out, I realized that maybe there should just be a marshalling interface. Then there's be a single class. Brian. -- Brian de Alwis | Software Practices Lab | UBC | http://www.cs.ubc.ca/~bsd/ "Amusement to an observing mind is study." - Benjamin Disraeli |