View Javadoc

1   /*
2   
3       dsh-piccolo-tilemap  Piccolo2D tile map nodes and supporting classes.
4       Copyright (c) 2006-2013 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.piccolo.tilemap.io.impl;
25  
26  import java.awt.Image;
27  
28  import java.io.File;
29  import java.io.IOException;
30  import java.io.InputStream;
31  
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  import javax.xml.parsers.SAXParserFactory;
36  
37  import net.sf.stax.SAX2StAXAdaptor;
38  import net.sf.stax.StAXContentHandlerBase;
39  import net.sf.stax.StAXContext;
40  import net.sf.stax.StAXDelegationContext;
41  
42  import org.dishevelled.piccolo.sprite.Animations;
43  import org.dishevelled.piccolo.sprite.Sprite;
44  
45  import org.dishevelled.piccolo.tilemap.AbstractTileMap;
46  import org.dishevelled.piccolo.tilemap.SparseTileMap;
47  
48  import org.xml.sax.XMLReader;
49  import org.xml.sax.Attributes;
50  import org.xml.sax.InputSource;
51  import org.xml.sax.SAXException;
52  import org.xml.sax.ContentHandler;
53  
54  /**
55   * TMX Map Format tile map reader.  See
56   * <a href="https://github.com/bjorn/tiled/wiki/TMX-Map-Format">https://github.com/bjorn/tiled/wiki/TMX-Map-Format</a>.
57   *
58   * @author  Michael Heuer
59   * @version $Revision$ $Date$
60   */
61  public class TmxTileMapReader
62      extends AbstractTileMapReader
63  {
64      /** XML reader. */
65      private XMLReader xmlReader;
66  
67  
68      /**
69       * Create a new TMX Map Format tile map reader.
70       */
71      public TmxTileMapReader()
72      {
73          try
74          {
75              xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
76          }
77          catch (Exception e)
78          {
79              // ignore
80          }
81      }
82  
83  
84      @Override
85      public AbstractTileMap read(final InputStream inputStream) throws IOException
86      {
87          if (inputStream == null)
88          {
89              throw new IllegalArgumentException ("inputStream must not be null");
90          }
91          InputSource inputSource = new InputSource(inputStream);
92          MapHandler mapHandler = new MapHandler();
93          ContentHandler contentHandler = new SAX2StAXAdaptor(mapHandler);
94          xmlReader.setContentHandler(contentHandler);
95          try
96          {
97              xmlReader.parse(inputSource);
98          }
99          catch (SAXException e)
100         {
101             throw new IOException("could not read input stream", e);
102         }
103         return mapHandler.getTileMap();
104    }
105 
106     /**
107      * Map handler.
108      */
109     private static final class MapHandler
110         extends StAXContentHandlerBase
111     {
112         private long width;
113         private long height;
114         private double tileWidth;
115         private double tileHeight;
116         private SparseTileMap tileMap;
117         private List<Sprite> sprites;
118         // only works for files containing a single layer,
119         // only works for data elements in Tiled XML format (tile child elements)
120         private DataHandler dataHandler = new DataHandler();
121         // only works for files containing a single tileset
122         private TilesetHandler tilesetHandler = new TilesetHandler();
123 
124 
125         @Override
126         public void startElement(final String nsURI,
127                                  final String localName,
128                                  final String qName,
129                                  final Attributes attrs,
130                                  final StAXDelegationContext dctx)
131             throws SAXException
132         {
133             if ("tileset".equals(qName))
134             {
135                 dctx.delegate(tilesetHandler);
136             }
137             else if ("data".equals(qName))
138             {
139                 dctx.delegate(dataHandler);
140             }
141             else if ("map".equals(qName))
142             {
143                 width = Long.parseLong(attrs.getValue("width"));
144                 height = Long.parseLong(attrs.getValue("height"));
145                 tileWidth = Double.parseDouble(attrs.getValue("tilewidth"));
146                 tileHeight = Double.parseDouble(attrs.getValue("tileheight"));
147                 tileMap = new SparseTileMap(width, height, tileWidth, tileHeight);
148             }
149         }
150 
151         @Override
152         public void endElement(final String nsURI,
153                                final String localName,
154                                final String qName,
155                                final Object result,
156                                final StAXContext context)
157             throws SAXException
158         {
159             if ("tileset".equals(qName))
160             {
161                 sprites = (List<Sprite>) result;
162             }
163         }
164 
165         /**
166          * Return the tile map.
167          *
168          * @return the tile map
169          */
170         SparseTileMap getTileMap()
171         {
172             return tileMap;
173         }
174 
175         /**
176          * Data handler.
177          */
178         private class DataHandler
179             extends StAXContentHandlerBase
180         {
181             private long i;
182 
183             @Override
184             public void startTree(final StAXContext ctx) throws SAXException
185             {
186                 i = 0;
187             }
188 
189             @Override
190             public void startElement(final String nsURI,
191                                      final String localName,
192                                      final String qName,
193                                      final Attributes attrs,
194                                      final StAXDelegationContext dctx)
195                 throws SAXException
196             {
197                 if ("tile".equals(qName))
198                 {
199                     int gid = Integer.parseInt(attrs.getValue("gid"));
200                     if (gid > 0)
201                     {
202                         long x = i / width;
203                         long y = i % height;
204                         Sprite sprite = sprites.get(gid - 1);
205                         tileMap.setTileXY(x, y, sprite);
206                     }
207                     i++;
208                 }
209             }
210         }
211     }
212 
213     /**
214      * Tileset handler.
215      */
216     private static final class TilesetHandler
217         extends StAXContentHandlerBase
218     {
219         private String source;
220         private int width;
221         private int height;
222         private int tileWidth;
223         private int tileHeight;
224 
225         @Override
226         public void startElement(final String nsURI,
227                                  final String localName,
228                                  final String qName,
229                                  final Attributes attrs,
230                                  final StAXDelegationContext dctx)
231             throws SAXException
232         {
233             if ("image".equals(qName))
234             {
235                 source = attrs.getValue("source");
236                 width = Integer.parseInt(attrs.getValue("width"));
237                 height = Integer.parseInt(attrs.getValue("height"));
238             }
239             else if ("tileset".equals(qName))
240             {
241                 tileWidth = Integer.parseInt(attrs.getValue("tilewidth"));
242                 tileHeight = Integer.parseInt(attrs.getValue("tileheight"));
243             }
244         }
245 
246         @Override
247         public Object endTree(final StAXContext context)
248             throws SAXException
249         {
250             // only works for horizontal tilesets
251             try
252             {
253                 List<Image> tiles = Animations.createFrameList(new File(source), 0, 0, tileWidth, tileHeight, width / tileWidth);
254                 List<Sprite> tileSprites = new ArrayList<Sprite>(tiles.size());
255                 for (Image tile : tiles)
256                 {
257                     Sprite tileSprite = new Sprite(Animations.createAnimation(tile));
258                     tileSprite.setWidth(tileWidth);
259                     tileSprite.setHeight(tileHeight);
260                     tileSprites.add(tileSprite);
261                 }
262                 return tileSprites;
263             }
264             catch (IOException e)
265             {
266                 throw new SAXException("could not create tileset from source", e);
267             }
268         }
269     }
270 }