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 }