001/* 002 * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games. 003 * Copyright (C) 2000-2010 The Gridarta Developers. 004 * 005 * This program is free software; you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation; either version 2 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License along 016 * with this program; if not, write to the Free Software Foundation, Inc., 017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 018 */ 019 020package net.sf.gridarta.utils; 021 022import java.io.BufferedInputStream; 023import java.io.File; 024import java.io.IOException; 025import java.io.InputStream; 026import java.net.URL; 027import java.util.regex.Pattern; 028import javax.xml.parsers.DocumentBuilder; 029import javax.xml.parsers.DocumentBuilderFactory; 030import javax.xml.parsers.ParserConfigurationException; 031import javax.xml.xpath.XPath; 032import javax.xml.xpath.XPathFactory; 033import org.jetbrains.annotations.NotNull; 034import org.jetbrains.annotations.Nullable; 035import org.xml.sax.EntityResolver; 036import org.xml.sax.InputSource; 037 038/** 039 * Little helper class for XML, holds a {@link DocumentBuilder} and an {@link 040 * XPath} that are setup for Gridarta. 041 * @author Andreas Kirschbaum 042 */ 043public class XmlHelper { 044 045 /** 046 * The {@link Pattern} for matching the directory part of a system ID. 047 */ 048 @NotNull 049 private static final Pattern PATTERN_DIRECTORY_PART = Pattern.compile(".*/"); 050 051 /** 052 * DocumentBuilder. 053 */ 054 private final DocumentBuilder documentBuilder; 055 056 /** 057 * The XPath for using XPath. 058 */ 059 private final XPath xpath; 060 061 /** 062 * Initialize the XML engine. 063 * @throws ParserConfigurationException in case the xml parser couldn't be 064 * set up 065 */ 066 public XmlHelper() throws ParserConfigurationException { 067 // TODO: Change hard coded document builder factory to user configurable settings 068 final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 069 dbf.setCoalescing(true); 070 // dbf.setExpandEntityReferences(true); // true is default on this 071 dbf.setIgnoringComments(true); 072 dbf.setIgnoringElementContentWhitespace(true); 073 dbf.setNamespaceAware(true); 074 dbf.setValidating(true); 075 dbf.setXIncludeAware(true); 076 documentBuilder = dbf.newDocumentBuilder(); 077 documentBuilder.setEntityResolver(new GridartaEntityResolver()); 078 xpath = XPathFactory.newInstance().newXPath(); 079 } 080 081 /** 082 * Return the DocumentBuilder. 083 * @return the document builder 084 */ 085 public DocumentBuilder getDocumentBuilder() { 086 return documentBuilder; 087 } 088 089 /** 090 * Return the XPath for using XPath. 091 * @return the XPath 092 */ 093 public XPath getXPath() { 094 return xpath; 095 } 096 097 /** 098 * Implements an {@link EntityResolver} for looking up built-in .dtd files. 099 */ 100 private static class GridartaEntityResolver implements EntityResolver { 101 102 @Nullable 103 @Override 104 public InputSource resolveEntity(final String publicId, final String systemId) throws IOException { 105 if (systemId.endsWith(".xml")) { 106 return null; 107 } 108 109 InputSource inputSource = null; 110 final URL url = IOUtils.getResource(new File("system/dtd"), PATTERN_DIRECTORY_PART.matcher(systemId).replaceAll("")); 111 final InputStream inputStream = url.openStream(); 112 try { 113 final BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); 114 try { 115 inputSource = new InputSource(bufferedInputStream); 116 } finally { 117 if (inputSource == null) { 118 bufferedInputStream.close(); 119 } 120 } 121 } finally { 122 if (inputSource == null) { 123 inputStream.close(); 124 } 125 } 126 return inputSource; 127 } 128 } 129 130}