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    }