Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c8966cd

Browse files
authored
libtiled-java: TMXMapReader optimization (mapeditor#3004)
* Allow multiple TMXMapReader instances to be used while multi-threading, caching tilesets to the same set. * Use only a single Unmarshaller instance that understands all relevant classes. * Fixed issue with handling recursive external tileset references.
1 parent 11e5c29 commit c8966cd

2 files changed

Lines changed: 58 additions & 20 deletions

File tree

util/java/libtiled-java/src/main/java/org/mapeditor/io/TMXMapReader.java

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,29 @@ public class TMXMapReader {
8484
public static final long FLIPPED_VERTICALLY_FLAG = 0x0000000040000000L;
8585
public static final long FLIPPED_DIAGONALLY_FLAG = 0x0000000020000000L;
8686

87-
public static final long ALL_FLAGS = FLIPPED_HORIZONTALLY_FLAG
88-
| FLIPPED_VERTICALLY_FLAG
89-
| FLIPPED_DIAGONALLY_FLAG;
87+
public static final long ALL_FLAGS =
88+
FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG;
89+
90+
public final TMXMapReaderSettings settings = new TMXMapReaderSettings();
9091

9192
private Map map;
9293
private URL xmlPath;
9394
private String error;
9495
private final EntityResolver entityResolver = new MapEntityResolver();
9596
private TreeMap<Integer, TileSet> tilesetPerFirstGid;
96-
public final TMXMapReaderSettings settings = new TMXMapReaderSettings();
97-
private final java.util.Map<String, TileSet> cachedTilesets = new HashMap<>();
98-
private final java.util.Map<Class<?>, Unmarshaller> cachedUnmarshallers = new HashMap<>();
97+
98+
/**
99+
* Map of cached tilesets used when {@link TMXMapReaderSettings#reuseCachedTilesets} option is on.
100+
* TODO: case when multiple tilesets have same name but different sources
101+
* @see #setCachedTilesets(java.util.Map)
102+
*/
103+
private java.util.Map<String, TileSet> cachedTilesets;
104+
105+
/**
106+
* Unmarshaller capable of unmarshalling all classes available from context
107+
* @see #unmarshalClass(Node, Class)
108+
*/
109+
private final Unmarshaller unmarshaller;
99110

100111
public static final class TMXMapReaderSettings {
101112

@@ -105,7 +116,10 @@ public static final class TMXMapReaderSettings {
105116
/**
106117
* Constructor for TMXMapReader.
107118
*/
108-
public TMXMapReader() {
119+
public TMXMapReader() throws JAXBException {
120+
unmarshaller = JAXBContext.newInstance(
121+
Map.class, TileSet.class, Tile.class,
122+
AnimatedTile.class, ObjectGroup.class, ImageLayer.class).createUnmarshaller();
109123
}
110124

111125
String getError() {
@@ -160,14 +174,11 @@ private static double getDoubleAttribute(Node node, String attribname, double de
160174
}
161175

162176
private <T> T unmarshalClass(Node node, Class<T> type) throws JAXBException {
163-
Unmarshaller unmarshaller = cachedUnmarshallers.get(type);
164-
if (unmarshaller == null) {
165-
JAXBContext context = JAXBContext.newInstance(type);
166-
unmarshaller = context.createUnmarshaller();
167-
cachedUnmarshallers.put(type, unmarshaller);
168-
}
169-
JAXBElement<T> element = unmarshaller.unmarshal(node, type);
170-
return element.getValue();
177+
// we expect that all classes are already bounded to JAXBContext, so we don't need to create unmarshaller
178+
// dynamicaly cause it's kinda heavy operation
179+
// if you got exception wich tells that SomeClass is not known to this context - just add it to the list
180+
// passed to JAXBContext constructor
181+
return unmarshaller.unmarshal(node, type).getValue();
171182
}
172183

173184
private BufferedImage unmarshalImage(Node t, URL baseDir) throws IOException {
@@ -228,10 +239,7 @@ private TileSet unmarshalTilesetFile(InputStream in, URL file) throws Exception
228239
// There can be only one tileset in a .tsx file.
229240
tsNode = tsNodeList.item(0);
230241
if (tsNode != null) {
231-
set = unmarshalTileset(tsNode);
232-
if (set.getSource() != null) {
233-
System.out.println("Recursive external tilesets are not supported.");
234-
}
242+
set = unmarshalTileset(tsNode, true);
235243
set.setSource(file.toString());
236244
}
237245

@@ -244,9 +252,25 @@ private TileSet unmarshalTilesetFile(InputStream in, URL file) throws Exception
244252
}
245253

246254
private TileSet unmarshalTileset(Node t) throws Exception {
255+
return unmarshalTileset(t, false);
256+
}
257+
258+
/**
259+
* @param t xml node to begin unmarshalling from
260+
* @param isExternalTileset is this a node of external tileset located in separate tsx file
261+
*/
262+
private TileSet unmarshalTileset(Node t, boolean isExternalTileset) throws Exception {
247263
TileSet set = unmarshalClass(t, TileSet.class);
248264

249265
String source = set.getSource();
266+
// if we have a "source" attribute in the external tileset - we ignore it and display a warning
267+
if (source != null && isExternalTileset) {
268+
source = null;
269+
set.setSource(null);
270+
System.out.printf("Warning: recursive external tilesets are not supported - " +
271+
"ignoring source option for tileset %s%n", set.getName());
272+
}
273+
250274
if (source != null) {
251275
source = replacePathSeparator(source);
252276
URL url = URLHelper.resolve(xmlPath, source);
@@ -268,6 +292,10 @@ private TileSet unmarshalTileset(Node t) throws Exception {
268292
final String name = getAttributeValue(t, "name");
269293

270294
if (settings.reuseCachedTilesets) {
295+
if (cachedTilesets == null) {
296+
cachedTilesets = new HashMap<>();
297+
}
298+
271299
set = cachedTilesets.get(name);
272300
if (set != null)
273301
return set;
@@ -1041,4 +1069,12 @@ private String replacePathSeparator(String path) {
10411069
return path;
10421070
return path.replace("/", File.separator);
10431071
}
1072+
1073+
/**
1074+
* The ability to set cachedTilesets allows cached tilesets to be shared across multiple readers,
1075+
* increasing read speed in a multithreaded environment.
1076+
*/
1077+
public void setCachedTilesets(java.util.Map<String, TileSet> cachedTilesets) {
1078+
this.cachedTilesets = cachedTilesets;
1079+
}
10441080
}

util/java/libtiled-java/src/test/java/org/mapeditor/io/MapReaderTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@
4747
import org.mapeditor.core.TileLayer;
4848
import org.mapeditor.core.TileSet;
4949

50+
import javax.xml.bind.JAXBException;
51+
5052
public class MapReaderTest {
5153

5254
@Test
53-
public void testAcceptValidFilenames() {
55+
public void testAcceptValidFilenames() throws JAXBException {
5456
// Arrange
5557
TMXMapReader reader = new TMXMapReader();
5658

0 commit comments

Comments
 (0)