001 package components; 002 003 import java.io.File; 004 import java.io.FileOutputStream; 005 import java.io.IOException; 006 import java.util.Collections; 007 import java.util.HashMap; 008 import java.util.HashSet; 009 import java.util.Map; 010 import java.util.Set; 011 import java.util.concurrent.ConcurrentHashMap; 012 013 import javax.xml.XMLConstants; 014 import javax.xml.parsers.DocumentBuilder; 015 import javax.xml.parsers.DocumentBuilderFactory; 016 import javax.xml.parsers.ParserConfigurationException; 017 import javax.xml.transform.Source; 018 import javax.xml.transform.dom.DOMSource; 019 import javax.xml.transform.stream.StreamSource; 020 import javax.xml.validation.Schema; 021 import javax.xml.validation.SchemaFactory; 022 import javax.xml.validation.Validator; 023 024 import org.w3c.dom.Document; 025 import org.w3c.dom.Element; 026 import org.xml.sax.SAXException; 027 028 import swingui.MainGUI; 029 030 import com.sun.org.apache.xml.internal.serialize.OutputFormat; 031 import com.sun.org.apache.xml.internal.serialize.XMLSerializer; 032 033 /** 034 * <p> 035 * XMLWriter is used to take a gamespace and a file or filename, and write the gamespace 036 * information to that file. It then verfies that the information is a validate gb_level schema. 037 * It is muttable. 038 * </p> 039 * <p> 040 * Our output follows this format: 041 * </p> 042 * <board properties...> 043 * <ball> 044 * <ball> 045 * <ball> 046 * ... 047 * <gizmos> 048 * <gizmo types ...... > 049 * .... 050 * <gizmos/> 051 * <connections> 052 * <connect> 053 * ... 054 * <keyConnect> 055 * ... 056 * <connections/> 057 * </board> 058 * 059 * 060 * @specfield filname : String //this is the file name of the output xml file 061 */ 062 public class XMLWriter { 063 private static Document xmlout; 064 065 /** 066 * This overloads writeXML. It is for convience. Takes File f, and uses its aboslute path 067 * to call writeXML(GameSpace,String); 068 * @throws IOException 069 * @throws SAXException 070 * @throws ParserConfigurationException 071 * @requires g and f != null 072 */ 073 public void writeXML(GameSpace g, File f) throws ParserConfigurationException, SAXException, IOException { 074 writeXML(g,f.getAbsolutePath()); 075 } 076 077 /** 078 * Given a namespace, and a filename, will write the information g to name in XMLFormat 079 * and make sure its a valid gb_level schema. 080 * @throws ParserConfigurationException 081 * @throws SAXException 082 * @throws IOException 083 * @requires g and name != null 084 * @modifies xmlout, put not in anyway that will effect future uses of this object 085 */ 086 public void writeXML(GameSpace g, String name) throws ParserConfigurationException, SAXException, IOException { 087 088 synchronized (g) { 089 090 //Get the balls 091 Set<GameObject> ballObjs = g.getBalls(); 092 093 //Get just the objects that aren't balls, or walls 094 Set<GameObject> allObjs = g.getObjects(); 095 //filter out the stuff we don't want 096 allObjs = filterObject(allObjs, GameObjectClassification.BALL); 097 allObjs = filterObject(allObjs, GameObjectClassification.WALL); 098 // //System.out.println("Balls=" + ballObjs.toString()); 099 // for(GameObject obj : allObjs) { 100 // Map<String,String> m = new HashMap<String,String>(); 101 // obj.getBasicPropertyMap(m); 102 // //System.out.println("Thing=" + m.toString()); 103 // } 104 105 106 DocumentBuilderFactory dbfact = DocumentBuilderFactory.newInstance(); 107 DocumentBuilder db = dbfact.newDocumentBuilder(); 108 xmlout = db.newDocument(); 109 110 //<board> 111 Element board = xmlout.createElement("board"); 112 board.setAttribute("xGravity", Double.toString(g.getSettings().getGravity().x())); 113 board.setAttribute("yGravity", Double.toString(g.getSettings().getGravity().y())); 114 board.setAttribute("zGravity", Double.toString(g.getSettings().getGravity().z())); 115 board.setAttribute("friction1", Double.toString(g.getSettings().getMu())); 116 board.setAttribute("friction2", Double.toString(g.getSettings().getMu2())); 117 118 writeObjects(board,ballObjs); 119 120 //<gizmos> 121 Element gameobjects = xmlout.createElement("gizmos"); 122 writeObjects(gameobjects, allObjs); 123 board.appendChild(gameobjects); 124 125 //<connections> 126 Element connections = xmlout.createElement("connections"); 127 128 //<connection> tags 129 for(GameObject go : g.getObjects()) { 130 for(GameObject t : go.getTargets()) { 131 Map<String,String> m = new HashMap<String,String>(); 132 m.put("sourceGizmo",go.getName()); 133 m.put("targetGizmo",t.getName()); 134 Element connect = xmlout.createElement("connect"); 135 writeMap(connect, m); 136 connections.appendChild(connect); 137 138 } 139 } 140 141 //<keyConnect> 142 KeyRegistry r = g.getRegistry(); 143 for (Integer i : r.getAllDownKeys()) { 144 for (GameObject o : r.getDown(i.intValue())) { 145 Map<String,String> m = new HashMap<String,String>(); 146 Element keyconnect = xmlout.createElement("keyConnect"); 147 m.put("key",""+i); 148 m.put("keyDirection","down"); 149 m.put("targetGizmo", o.getName()); 150 writeMap(keyconnect, m); 151 connections.appendChild(keyconnect); 152 } 153 } 154 for (Integer i : r.getAllUpKeys()) { 155 for (GameObject o : r.getUp(i.intValue())) { 156 Map<String,String> m = new HashMap<String,String>(); 157 Element keyconnect = xmlout.createElement("keyConnect"); 158 m.put("key",""+i); 159 m.put("keyDirection","up"); 160 m.put("targetGizmo", o.getName()); 161 writeMap(keyconnect, m); 162 connections.appendChild(keyconnect); 163 } 164 } 165 166 board.appendChild(connections); 167 168 xmlout.appendChild(board); 169 170 SchemaFactory fact = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 171 172 Source schemedata = new StreamSource(MainGUI.getStream("gb_level.xsd")); 173 Schema schema = fact.newSchema(schemedata); 174 175 Validator validate = schema.newValidator(); 176 validate.validate(new DOMSource(xmlout)); 177 178 OutputFormat out = new OutputFormat(); 179 out.setIndenting(true); 180 out.setLineSeparator("\n"); 181 FileOutputStream outfile = new FileOutputStream(name); 182 XMLSerializer serial = new XMLSerializer(outfile, out); 183 184 serial.serialize(xmlout); 185 186 187 } 188 189 190 } 191 192 /** 193 * @requires s is a valid s of game objects, and cname is a valid string name of 194 * a recognized class 195 */ 196 private static Set<GameObject> filterObject(Set<GameObject> s, GameObjectClassification cc) { 197 198 Set<GameObject> toReturn = new HashSet<GameObject>(); 199 for (GameObject go : s) { 200 if (cc != go.getGOClassification() ) { 201 toReturn.add(go); 202 } 203 } 204 return Collections.unmodifiableSet(toReturn); 205 } 206 207 208 209 210 /** 211 * Formats objs for writing to xml 212 * @requires this.gameobjects is not null,a dn objs is not null 213 * @modifies board 214 */ 215 public void writeObjects(Element parent, Set<GameObject> objs) { 216 for(GameObject b : objs) { 217 Element obj = xmlout.createElement(b.getGOClassification().getXMLName()); 218 Map<String,String> map = new ConcurrentHashMap<String,String>(); 219 b.getBasicPropertyMap(map); 220 for(String attr: map.keySet()) { 221 obj.setAttribute(attr, map.get(attr)); 222 } 223 parent.appendChild(obj); 224 } 225 } 226 227 228 /** 229 * This takes a map, and an element, and writes every key value pair in map as attributes in that element 230 * @requires parent and m not equal null 231 * 232 */ 233 public void writeMap(Element parent, Map<String,String> m) { 234 for (String attr: m.keySet()) { 235 parent.setAttribute(attr, m.get(attr)); 236 } 237 } 238 239 }