001 package components; 002 003 004 import java.io.File; 005 import java.io.IOException; 006 import java.util.ArrayList; 007 import java.util.List; 008 import java.util.Map; 009 import java.util.concurrent.ConcurrentHashMap; 010 011 import javax.xml.XMLConstants; 012 import javax.xml.parsers.ParserConfigurationException; 013 import javax.xml.parsers.SAXParser; 014 import javax.xml.parsers.SAXParserFactory; 015 import javax.xml.validation.Schema; 016 import javax.xml.validation.SchemaFactory; 017 018 import org.xml.sax.Attributes; 019 import org.xml.sax.SAXException; 020 import org.xml.sax.helpers.DefaultHandler; 021 022 import physics3d.Vect3; 023 024 /**<p> 025 * This class is used to read xml data and load it into a GameSpace. Given a string name 026 * this class can be used to parse the xml in that file, and load it into the gamespace. Defautl setings 027 * are used if parameters are missing. The XMLReader also supports any arbitary object property, 028 * but it will only be set if it actually used in the object being loaded. 029 * </p> 030 * This class has no specfields 031 */ 032 public class XMLReader extends DefaultHandler { 033 //FIELDS 034 private GameSpace gamespace; 035 private GameSettings settings; 036 037 038 /** 039 * Constructs a new XMLReader object 040 */ 041 public XMLReader() { 042 settings = new GameSettings(); 043 //Hmm, unsure about this, this "new gamesettings" will be lost once loadBoard loads 044 //this.settings into GameSettings 045 gamespace = new GameSpace(new GameSettings()); 046 } 047 048 049 /** 050 * Given a valid XML file returns a GameSpace that contains 051 * everything. 052 * @throws SAXException 053 * @throws ParserConfigurationException 054 * @throws IOException 055 * 056 *@requires file is the location of a valid xml file 057 */ 058 public GameSpace readXMLFile(String file) throws SAXException, ParserConfigurationException, IOException { 059 // Use an ourselves as the SAX event handler 060 DefaultHandler handler = this; 061 062 // Use the default (non-validating) parser 063 SAXParserFactory factory = SAXParserFactory.newInstance(); 064 factory.setValidating(true); 065 factory.setNamespaceAware(true); 066 067 //StreamSource streamsource = new StreamSource("gb_level.xsd"); 068 // build an XSD-aware SchemaFactory 069 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 070 071 Schema schemaXSD = schemaFactory.newSchema(); 072 factory.setSchema(schemaXSD); 073 074 // Parse the input 075 SAXParser saxParser = factory.newSAXParser(); 076 saxParser.parse(new File(file), handler); 077 078 return this.gamespace; 079 080 081 } 082 083 084 085 086 /** 087 * Reads the title of an XMLtag and determines the appropiate objects to add to gamespace, 088 * or the appropriate settings to modify. This is based on the assumping that the filename 089 * follows the proper format, which was proved in readXML 090 * <br> 091 * This is an inherited method from DefaultHandler 092 */ 093 public void startElement(String namespaceURI, 094 String sName, // simple name (localName) 095 String qName, // qualified name 096 Attributes attrs) 097 throws SAXException 098 { 099 100 List<String> gnames = new ArrayList<String>(); 101 GameObjectClassification[] glist = GameObjectClassification.values(); 102 for (GameObjectClassification e : glist) { 103 gnames.add(e.toString().toLowerCase()); 104 } 105 106 if (sName.equals("board")) 107 loadGameBoard(attrs); 108 else if (sName.equals("connect")) 109 loadConnect(attrs); 110 else if (sName.equals("keyConnect")) 111 loadKeyConnect(attrs); 112 else if (gnames.contains(sName.toLowerCase())) 113 loadGameObject(GameObjectClassification.valueOf(sName.toUpperCase()), attrs); 114 } 115 116 /** 117 * Given a set of attributes, will load the appropriate game settings into a gameboard. 118 * @require valid attrs 119 */ 120 private void loadGameBoard(Attributes attrs) { 121 settings.setMu(0.025); 122 settings.setMu2(0.025); 123 settings.setGravity(new Vect3(0,25,0)); 124 settings.setFPS(32); 125 126 double x=0,y=1,z=0; 127 128 for (int i = 0; i < attrs.getLength(); i++) { 129 String aName = attrs.getLocalName(i); // Attr name 130 if ("".equals(aName)) aName = attrs.getQName(i); 131 132 133 if (aName.equals("gravityZ")) { 134 z=Double.valueOf(attrs.getValue(i)); 135 } else if (aName.equals("gravityY")) { 136 y=Double.valueOf(attrs.getValue(i)); 137 } else if (aName.equals("gravityX")) { 138 x=Double.valueOf(attrs.getValue(i)); 139 } else if (aName.equals("friction1")) { 140 settings.setMu(Double.valueOf(attrs.getValue(i))); 141 } else if (aName.equals("friction2")) { 142 settings.setMu2(Double.valueOf(attrs.getValue(i))); 143 } else if (aName.equals("FPS")) { 144 settings.setFPS(Integer.valueOf(attrs.getValue(i))); 145 } 146 } 147 148 settings.setGravity(new Vect3(x,y,z)); 149 gamespace.loadSettings(settings); 150 151 } 152 153 154 /** 155 * Given a gizmotype, and it's cooresponding attribures, this loads that gizmo into this.gamespace 156 * @require no passed values are null 157 */ 158 private void loadGameObject(GameObjectClassification gtype, Attributes attrs) { 159 Map<String,String> props = mapAttr(attrs); 160 161 try { 162 gamespace.loadObject(gtype, props); 163 } 164 catch(Exception e) { 165 e.printStackTrace(); 166 throw new RuntimeException("Error, unable to load object " + gtype.toString() + " error: "); 167 } 168 } 169 170 /** 171 * Given attrs, make the specificed connection between elements 172 * @requires valid attributes stored in attrs 173 */ 174 private void loadKeyConnect(Attributes attrs) { 175 Map<String,String> props = mapAttr(attrs); 176 int key = -1; 177 String direction = ""; 178 GameObject target = null; 179 try { 180 key = Integer.parseInt(props.get("key")); 181 direction = props.get("keydirection"); 182 target = gamespace.getByName(props.get("targetgizmo")); 183 //System.err.println("key=" + key+ ", dir="+direction+", tgt="+target); 184 if (direction.equalsIgnoreCase("down")) { 185 System.err.println("adding down press"); 186 gamespace.addDownKey(key, target); 187 } 188 else if (direction.equalsIgnoreCase("up")) 189 gamespace.addUpKey(key, target); 190 else 191 throw new RuntimeException("Unrecognized direction: " + direction); 192 193 } catch (Exception e) { 194 throw new RuntimeException("unable to key connect: "+ e); 195 } 196 197 } 198 199 /** 200 * Given attrs, make the specificed connection between elements 201 * @requires valid attributes stored in attrs 202 */ 203 private void loadConnect(Attributes attrs) { 204 Map<String,String> props = mapAttr(attrs); 205 206 GameObject src = null; 207 GameObject tgt = null; 208 try { 209 //System.out.println(props); 210 src = gamespace.getByName(props.get("sourcegizmo")); 211 tgt = gamespace.getByName(props.get("targetgizmo")); 212 src.addTarget(tgt); 213 } catch(Exception e) { 214 throw new RuntimeException("Error, unable to connect " +src+ " and " +tgt + ": "+ e); 215 } 216 } 217 218 /** 219 * @requires attrs is a map of atributes 220 * @return a hash map maping names to values as represented in attrs 221 */ 222 private Map<String, String> mapAttr(Attributes attrs) { 223 Map<String, String> props = new ConcurrentHashMap<String,String>(); 224 225 for (int c=0; c < attrs.getLength(); c++) { 226 String key = ""; 227 if (attrs.getLocalName(c).equals("")) { 228 key = attrs.getLocalName(c).toLowerCase(); 229 } 230 else { 231 key = attrs.getQName(c).toLowerCase(); 232 } 233 String value = attrs.getValue(c).toLowerCase(); 234 props.put(key, value); 235 236 if (key.equals("")) { 237 throw new RuntimeException("Error, no key on interaction " + c); 238 } 239 } 240 return props; 241 } //mapAttr 242 243 }