1 /*-
2  * #%L
3  * This file is part of libtiled-java.
4  * %%
5  * Copyright (C) 2004 - 2020 Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
6  * Copyright (C) 2004 - 2020 Adam Turk <aturk@biggeruniverse.com>
7  * Copyright (C) 2016 - 2020 Mike Thomas <mikepthomas@outlook.com>
8  * %%
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright notice,
13  *    this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright notice,
15  *    this list of conditions and the following disclaimer in the documentation
16  *    and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  * #L%
30  */
31 package org.mapeditor.io;
32 
33 import java.io.File;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.net.URL;
37 import java.util.List;
38 import java.util.logging.Level;
39 import java.util.logging.Logger;
40 import java.util.zip.GZIPInputStream;
41 
42 import javax.xml.bind.JAXBContext;
43 import javax.xml.bind.JAXBElement;
44 import javax.xml.bind.JAXBException;
45 import javax.xml.bind.Unmarshaller;
46 import javax.xml.stream.XMLEventReader;
47 import javax.xml.stream.XMLInputFactory;
48 import javax.xml.stream.XMLStreamException;
49 
50 import org.mapeditor.core.Map;
51 import org.mapeditor.core.TileSet;
52 
53 /**
54  * The standard map reader for TMX files. Supports reading .tmx, .tmx.gz and
55  * *.tsx files.
56  *
57  * @version 1.4.2
58  */
59 public class MapReader {
60 
61     /**
62      * readMap.
63      *
64      * @param in a {@link java.io.InputStream} object.
65      * @param xmlPath a {@link java.lang.String} object.
66      * @return a {@link org.mapeditor.core.Map} object.
67      * @throws java.io.IOException if any.
68      */
readMap(InputStream in, String xmlPath)69     public Map readMap(InputStream in, String xmlPath) throws IOException {
70         Map unmarshalledMap = unmarshal(in, Map.class);
71         return buildMap(unmarshalledMap, xmlPath);
72     }
73 
74     /**
75      * readMap.
76      *
77      * @param filename a {@link java.lang.String} object.
78      * @return a {@link org.mapeditor.core.Map} object.
79      * @throws java.io.IOException if any.
80      */
readMap(String filename)81     public Map readMap(String filename) throws IOException {
82         int fileSeparatorIndex = filename.lastIndexOf(File.separatorChar) + 1;
83         String xmlPath = makeUrl(filename.substring(0, fileSeparatorIndex));
84 
85         String xmlFile = makeUrl(filename);
86 
87         URL url = new URL(xmlFile);
88         InputStream is = url.openStream();
89 
90         // Wrap with GZIP decoder for .tmx.gz files
91         if (filename.endsWith(".gz")) {
92             is = new GZIPInputStream(is);
93         }
94 
95         return readMap(is, xmlPath);
96     }
97 
98     /**
99      * readTileset.
100      *
101      * @param in a {@link java.io.InputStream} object.
102      * @return a {@link org.mapeditor.core.TileSet} object.
103      */
readTileset(InputStream in)104     public TileSet readTileset(InputStream in) {
105         return unmarshal(in, TileSet.class);
106     }
107 
108     /**
109      * readTileset.
110      *
111      * @param filename a {@link java.lang.String} object.
112      * @return a {@link org.mapeditor.core.TileSet} object.
113      * @throws java.io.IOException if any.
114      */
readTileset(String filename)115     public TileSet readTileset(String filename) throws IOException {
116         String xmlFile = makeUrl(filename);
117         URL url = new URL(xmlFile);
118         return readTileset(url.openStream());
119     }
120 
buildMap(Map map, String xmlPath)121     private Map buildMap(Map map, String xmlPath) throws IOException {
122         List<TileSet> tilesets = map.getTileSets();
123         for (int i = 0; i < tilesets.size(); i++) {
124             TileSet tileset = tilesets.get(i);
125             String tileSetSource = tileset.getSource();
126             if (tileSetSource != null) {
127                 int firstGid = tileset.getFirstgid();
128                 tileset = readTileset(xmlPath + tileSetSource);
129                 tileset.setFirstgid(firstGid);
130                 tileset.setSource(tileSetSource);
131                 tilesets.set(i, tileset);
132             }
133         }
134         return map;
135     }
136 
makeUrl(String filename)137     private String makeUrl(String filename) {
138         final String url;
139         if (filename.indexOf("://") > 0 || filename.startsWith("file:")) {
140             url = filename;
141         } else {
142             url = new File(filename).toURI().toString();
143         }
144         return url;
145     }
146 
unmarshal(InputStream in, Class<T> type)147     private <T> T unmarshal(InputStream in, Class<T> type) {
148         try {
149             XMLInputFactory factory = XMLInputFactory.newInstance();
150             XMLEventReader reader = factory.createXMLEventReader(in);
151 
152             JAXBContext context = JAXBContext.newInstance(type);
153             Unmarshaller unmarshaller = context.createUnmarshaller();
154 
155             JAXBElement<T> element = unmarshaller.unmarshal(reader, type);
156             return element.getValue();
157         } catch (XMLStreamException | JAXBException ex) {
158             Logger.getLogger(MapReader.class.getName()).log(Level.SEVERE, null, ex);
159             return null;
160         }
161     }
162 }
163