From: Erik V. <ev...@us...> - 2010-02-03 20:17:41
|
Update of /cvsroot/rails/18xx/tools In directory sfp-cvsdas-1.v30.ch3.sourceforge.com:/tmp/cvs-serv1357/tools Added Files: ConvertTilesXML.java Util.java MakeGameTileSets.java XmlUtils.java Log Message: Home base on green OO tile can now be chosen in first turn --- NEW FILE: XmlUtils.java --- /* $Header: /cvsroot/rails/18xx/tools/XmlUtils.java,v 1.1 2010/02/03 20:16:38 evos Exp $*/ package tools; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.*; import org.xml.sax.SAXException; import rails.game.ConfigurationException; /** * Booch utility class providing helper functions for working with XML. */ public final class XmlUtils { /** * No-args private constructor, to prevent (meaningless) construction of one * of these. */ private XmlUtils() {} /** * Extracts the String value of a given attribute from a NodeNameMap. * Returns null if no such attribute can be found. See * extractStringAttribute(NamedNodeMap nnp, String attrName, String * defaultValue) * * @param nnp the NodeNameMap to search for the Attribute * @param attrName the name of the attribute who's value is desired * @return the named attribute's value or null if absent. */ /** @deprecated */ public static String extractStringAttribute(NamedNodeMap nnp, String attrName) { return extractStringAttribute(nnp, attrName, null); } /** * Extracts the String value of a given attribute from a NodeNameMap. * Returns a default value if no such attribute can be found. * * @param nnp the NodeNameMap to search for the Attribute * @param attrName the name of the attribute who's value is desired * @param defaultValue the value to be returned if the attribute is absent. * @return the named attribute's value, or the default value if absent. */ /** @deprecated */ public static String extractStringAttribute(NamedNodeMap nnp, String attrName, String defaultValue) { if (nnp == null) return defaultValue; Node nameAttr = nnp.getNamedItem(attrName); if (nameAttr == null) return defaultValue; return nameAttr.getNodeValue(); } /** * Extracts the integer value of a given attribute from a NodeNameMap. * Returns zero if no such attribute can be found. * * @see * @param nnp the NodeNameMap to search for the Attribute * @param attrName the name of the attribute who's value is desired * @return the named attribute's value, or zero if absent. */ /** @deprecated */ public static int extractIntegerAttribute(NamedNodeMap nnp, String attrName) throws ConfigurationException { return extractIntegerAttribute(nnp, attrName, 0); } /** * Extracts the integer value of a given attribute from a NodeNameMap. * Returns a default value if no such attribute can be found. * * @see * @param nnp the NodeNameMap to search for the Attribute * @param attrName the name of the attribute who's value is desired. * @param defaultValue The value returned if the attribute is absent. * @return the named attribute's value or the dedault value. */ /** @deprecated */ public static int extractIntegerAttribute(NamedNodeMap nnp, String attrName, int defaultValue) throws ConfigurationException { if (nnp == null) return defaultValue; Node nameAttr = nnp.getNamedItem(attrName); if (nameAttr == null) { return defaultValue; } String value = nameAttr.getNodeValue(); try { return Integer.parseInt(value); } catch (Exception e) { throw new ConfigurationException("Invalid integer value: " + value, e); } } /** * Extracts the boolean value of a given attribute from a NodeNameMap. Any * string that starts with T or t (for "true") or Y or y (for "yes") is * considered to represent true, all other values will produce false. * * @param nnp The NodeNameMap to search for the Attribute * @param attrName The name of the attribute who's value is desired * @return The named attribute's value, or false if absent. */ /** @deprecated */ public static boolean extractBooleanAttribute(NamedNodeMap nnp, String attrName) throws ConfigurationException { return extractBooleanAttribute(nnp, attrName, false); } /** * Extracts the boolean value of a given attribute from a NodeNameMap. * Returns a default value if no such attribute can be found. Any string * that starts with T or t (for "true") or Y or y (for "yes") is considered * to represent true, all other values will produce false. * * @param nnp The NodeNameMap to search for the Attribute * @param attrName The name of the attribute who's value is desired * @param defaultValue The value returned if the attribute is absent. * @return The named attribute's value or the default value. */ /** @deprecated */ public static boolean extractBooleanAttribute(NamedNodeMap nnp, String attrName, boolean defaultValue) throws ConfigurationException { if (nnp == null) return defaultValue; Node nameAttr = nnp.getNamedItem(attrName); if (nameAttr == null) { return defaultValue; } String value = nameAttr.getNodeValue(); return value.matches("^[TtYy].*"); } /** @deprecated */ public static int[] extractIntegerArrayAttribute(NamedNodeMap nnp, String attrName) throws ConfigurationException { if (nnp == null) return null; Node nameAttr = nnp.getNamedItem(attrName); if (nameAttr == null) return new int[0]; String[] values = nameAttr.getNodeValue().split(","); int[] result = new int[values.length]; int i = 0; try { for (i = 0; i < values.length; i++) { result[i] = Integer.parseInt(values[i]); } } catch (NumberFormatException e) { throw new ConfigurationException("Invalid integer '" + values[i] + "' in attribute '" + attrName + "'"); } return result; } /** * Opens and parses an xml file. Searches the root level of the file for an * element with the supplied name. * * @param fileName the name of the file to open * @param elementName the name of the element to find * @return the named element in the named file * @throws ConfigurationException if there is any problem opening and * parsing the file, or if the file does not contain a top level element * with the given name. */ public static Element findElementInFile(String fileName, String elementName) throws ConfigurationException { Document doc = null; try { // Step 1: create a DocumentBuilderFactory and setNamespaceAware DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); // Step 2: create a DocumentBuilder DocumentBuilder db = dbf.newDocumentBuilder(); // Step 3: parse the input file to get a Document object doc = db.parse(Util.getStreamForFile(fileName)); } catch (ParserConfigurationException e) { throw new ConfigurationException("Could not read/parse " + fileName + " to find element " + elementName, e); } catch (SAXException e) { throw new ConfigurationException("Could not read/parse " + fileName + " to find element " + elementName, e); } catch (IOException e) { throw new ConfigurationException("Could not read/parse " + fileName + " to find element " + elementName, e); } if (doc == null) { throw new ConfigurationException("Cannot find file " + fileName); } // Now find the named Element NodeList nodeList = doc.getChildNodes(); for (int iNode = 0; (iNode < nodeList.getLength()); iNode++) { Node childNode = nodeList.item(iNode); if ((childNode != null) && (childNode.getNodeName().equals(elementName)) && (childNode.getNodeType() == Node.ELEMENT_NODE)) { return (Element) childNode; } } throw new ConfigurationException("Could not find " + elementName + " in " + fileName); } } --- NEW FILE: Util.java --- /* $Header: /cvsroot/rails/18xx/tools/Util.java,v 1.1 2010/02/03 20:16:38 evos Exp $*/ package tools; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public final class Util { /** * No-args private constructor, to prevent (meaningless) construction of one * of these. */ private Util() {} public static boolean hasValue(String s) { return s != null && !s.equals(""); } /** * Open an input stream from a file, which may exist as a physical file or * in a JAR file. The name must be valid for both options. * * @author Erik Vos */ public static InputStream getStreamForFile(String fileName) throws IOException { File file = new File(fileName); if (file.exists()) { return new FileInputStream(file); } else { return null; } } } --- NEW FILE: ConvertTilesXML.java --- package tools; import java.io.*; import java.util.*; import java.util.regex.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.*; import rails.game.ConfigurationException; import rails.util.Util; import tools.XmlUtils; /** * Convert an XML tile dictionary, as created by Marco Rocci's Tile Designer, to * an XML file for use in Rails 18xx. <p> The default names are: */ public class ConvertTilesXML { private static String inputFilePath = "tiles/TileDictionary.xml"; private static String outputFilePath = "tiles/Tiles.xml"; private static Map<String, String> colourMap, gaugeMap, sidesMap, cityMap; private static Map<String, String[]> stationMap; private static Map<String, String> junctionPosition; /** Maps non-edge non-station junctions to tracks ending there. */ private static Map<String, List<Element>> unresolvedTrack; /** Maps tracks to edge/station junctions */ private static Map<Element, String> resolvedTrack; private static Pattern namePattern = Pattern.compile("^(\\d+)(/.*)?"); Document outputDoc; Element outputJunction; String tileNo; String colour; static { colourMap = new HashMap<String, String>(); colourMap.put("tlYellow", "yellow"); colourMap.put("tlGreen", "green"); colourMap.put("tlBrown", "brown"); colourMap.put("tlGray", "gray"); colourMap.put("tlOffMap", "red"); colourMap.put("tlMapFixed", "fixed"); colourMap.put("tlMapUpgradableToYellow", "white"); colourMap.put("tlMapUpgradableToGreen", "yellow"); colourMap.put("tlMapUpgradableToBrown", "green"); stationMap = new HashMap<String, String[]>(); stationMap.put("jtWhistlestop", new String[] { "Town", "0" }); stationMap.put("jtCity", new String[] { "City", "1" }); stationMap.put("jtDoubleCity", new String[] { "City", "2" }); stationMap.put("jtTripleCity", new String[] { "City", "3" }); stationMap.put("jtQuadrupleCity", new String[] { "City", "4" }); stationMap.put("jtNone", new String[] { "", "0" }); // Note: an additional station type is "Pass". gaugeMap = new HashMap<String, String>(); gaugeMap.put("ctNormal", "normal"); gaugeMap.put("ctSmall", "narrow"); gaugeMap.put("ctUniversal", "dual"); gaugeMap.put("ctTunnel", "normal"); gaugeMap.put("ctMountain", "normal"); // 1841 Pass: Station type is changed to Pass. sidesMap = new HashMap<String, String>(); sidesMap.put("tp4SideA", "side0"); sidesMap.put("tp4SideB", "side1"); sidesMap.put("tp4SideC", "side2"); sidesMap.put("tp4SideD", "side3"); sidesMap.put("tp4SideE", "side4"); sidesMap.put("tp4SideF", "side5"); cityMap = new HashMap<String, String>(); cityMap.put("tpCenter", "0"); cityMap.put("tp1SideA", "001"); cityMap.put("tp1CornerA", "051"); cityMap.put("tp1SideB", "101"); cityMap.put("tp1CornerB", "151"); cityMap.put("tp1SideC", "201"); cityMap.put("tp1CornerC", "251"); cityMap.put("tp1SideD", "301"); cityMap.put("tp1CornerD", "351"); cityMap.put("tp1SideE", "401"); cityMap.put("tp1CornerE", "451"); cityMap.put("tp1SideF", "501"); cityMap.put("tp1CornerF", "551"); cityMap.put("tp2SideA", "002"); cityMap.put("tp2CornerA", "052"); cityMap.put("tp2SideB", "102"); cityMap.put("tp2CornerB", "152"); cityMap.put("tp2SideC", "202"); cityMap.put("tp2CornerC", "252"); cityMap.put("tp2SideD", "302"); cityMap.put("tp2CornerD", "352"); cityMap.put("tp2SideE", "402"); cityMap.put("tp2CornerE", "452"); cityMap.put("tp2SideF", "502"); cityMap.put("tp2CornerF", "552"); cityMap.put("tp3SideA", "003"); cityMap.put("tp3CornerA", "053"); cityMap.put("tp3SideB", "103"); cityMap.put("tp3CornerB", "153"); cityMap.put("tp3SideC", "203"); cityMap.put("tp3CornerC", "253"); cityMap.put("tp3SideD", "303"); cityMap.put("tp3CornerD", "353"); cityMap.put("tp3SideE", "403"); cityMap.put("tp3CornerE", "453"); cityMap.put("tp3SideF", "503"); cityMap.put("tp3CornerF", "553"); cityMap.put("tpCurve1RightA", "006"); cityMap.put("tpCurve2RightA", "007"); cityMap.put("tpCurve2LeftA", "008"); cityMap.put("tpCurve1LeftA", "009"); cityMap.put("tpCurve1RightB", "106"); cityMap.put("tpCurve2RightB", "107"); cityMap.put("tpCurve2LeftB", "108"); cityMap.put("tpCurve1LeftB", "109"); cityMap.put("tpCurve1RightC", "206"); cityMap.put("tpCurve2RightC", "207"); cityMap.put("tpCurve2LeftC", "208"); cityMap.put("tpCurve1LeftC", "209"); cityMap.put("tpCurve1RightD", "306"); cityMap.put("tpCurve2RightD", "307"); cityMap.put("tpCurve2LeftD", "308"); cityMap.put("tpCurve1LeftD", "309"); cityMap.put("tpCurve1RightE", "406"); cityMap.put("tpCurve2RightE", "407"); cityMap.put("tpCurve2LeftE", "408"); cityMap.put("tpCurve1LeftE", "409"); cityMap.put("tpCurve1RightF", "506"); cityMap.put("tpCurve2RightF", "507"); cityMap.put("tpCurve2LeftF", "508"); cityMap.put("tpCurve1LeftF", "509"); } public static void main(String[] args) { if (args != null) { if (args.length > 0) inputFilePath = args[0]; if (args.length > 1) outputFilePath = args[1]; } try { new ConvertTilesXML(); } catch (ConfigurationException e) { e.printStackTrace(); } } private ConvertTilesXML() throws ConfigurationException { Element inputTopElement = XmlUtils.findElementInFile(inputFilePath, "tiles"); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); DOMImplementation impl = builder.getDOMImplementation(); outputDoc = impl.createDocument(null, "Tiles", null); convertXML(inputTopElement, outputDoc); TransformerFactory.newInstance().newTransformer().transform( new DOMSource(outputDoc), new StreamResult(new FileOutputStream(new File( outputFilePath)))); } catch (Exception e) { throw new ConfigurationException("Document build error", e); } } private void convertXML(Element inputElement, Document outputDoc) throws ConfigurationException { NodeList children = inputElement.getElementsByTagName("tile"); for (int i = 0; i < children.getLength(); i++) { Element inputTile = (Element) children.item(i); Element outputTile = outputDoc.createElement("Tile"); outputDoc.getDocumentElement().appendChild(outputTile); convertTile(inputTile, outputTile); } } private void convertTile(Element inputTile, Element outputTile) throws ConfigurationException { String id = inputTile.getElementsByTagName("ID").item(0).getFirstChild().getNodeValue(); System.out.println(id); tileNo = id; outputTile.setAttribute("id", id); // int intId; try { Integer.parseInt(id); } catch (NumberFormatException e) { throw new ConfigurationException("Non-numeric ID: " + id, e); } String level = inputTile.getElementsByTagName("level").item(0).getFirstChild().getNodeValue(); colour = colourMap.get(level); if (colour == null) { throw new ConfigurationException("Unknown level: " + level); } else { outputTile.setAttribute("colour", colour); } String name = inputTile.getElementsByTagName("name").item(0).getFirstChild().getNodeValue(); Matcher m = namePattern.matcher(name); if (m.matches()) { outputTile.setAttribute("name", m.group(1)); } else outputTile.setAttribute("name", name); // The below does not work for "B+" /* * if (intId > 0) { throw new ConfigurationException("Tile with ID=" + * id + " has a name not starting with a number: " + name); } */ /* * Create map to hold the station 'identifiers', which are referred to * in the track definitions. */ junctionPosition = new HashMap<String, String>(); outputJunction = null; Element junctions = (Element) inputTile.getElementsByTagName("junctions").item(0); NodeList children = junctions.getElementsByTagName("junction"); for (int i = 0; i < children.getLength(); i++) { Element inputJunction = (Element) children.item(i); outputJunction = outputDoc.createElement("Station"); outputTile.appendChild(outputJunction); convertJunction(i, inputJunction, outputJunction); } unresolvedTrack = new HashMap<String, List<Element>>(); resolvedTrack = new HashMap<Element, String>(); Element connections = (Element) inputTile.getElementsByTagName("connections").item(0); children = connections.getElementsByTagName("connection"); for (int i = 0; i < children.getLength(); i++) { Element inputConnection = (Element) children.item(i); convertConnection(inputConnection, outputTile); } // Iterator it = unresolvedTrack.keySet().iterator(); String end1, end2; // while (it.hasNext()) for (String key : unresolvedTrack.keySet()) { // String key = (String) it.next(); List<Element> list = unresolvedTrack.get(key); Element[] ends = list.toArray(new Element[0]); if (ends.length <= 1) { throw new ConfigurationException("Loose end " + ends[0] + " in tile " + tileNo); } for (int i = 1; i < ends.length; i++) { end1 = resolvedTrack.get(ends[i]); if (end1 == null) { throw new ConfigurationException("Loose end " + ends[i] + " in tile " + tileNo); } for (int j = 0; j < i; j++) { end2 = resolvedTrack.get(ends[j]); if (end2 == null) { throw new ConfigurationException("Loose end " + ends[j] + " in tile " + tileNo); } Element outputConnection = outputDoc.createElement("Track"); outputConnection.setAttribute("gauge", ends[i].getAttribute("gauge")); outputConnection.setAttribute("from", end1); outputConnection.setAttribute("to", end2); outputTile.appendChild(outputConnection); } } } } private void convertJunction(int i, Element inputJunction, Element outputJunction) throws ConfigurationException { String cityId = "city" + (i + 1); outputJunction.setAttribute("id", cityId); String type = inputJunction.getElementsByTagName("junType").item(0).getFirstChild().getNodeValue(); String[] station = stationMap.get(type); if (station == null) { throw new ConfigurationException("Unknown junction type: " + type); } else { station = station.clone(); } /* * Off-map cities have the special type "OffMapCity" which does not * allow driving through. Original type "town" indicates that no token * can be placed. */ if (colour.equals("red")) { if (station[0].equals("Town")) station[1] = "0"; station[0] = "OffMapCity"; } outputJunction.setAttribute("type", station[0]); if (!station[1].equals("0")) { outputJunction.setAttribute("slots", station[1]); } // String[] p = (String[]) ((String[]) stationMap.get(type)).clone(); if (station == null) { throw new ConfigurationException("Unknown junction type: " + type); } // Junction revenue Element revenue = (Element) inputJunction.getElementsByTagName("revenue").item(0); if (revenue != null) { String value = revenue.getElementsByTagName("value").item(0).getFirstChild().getNodeValue(); outputJunction.setAttribute("value", value); } // Junction position String junctionPos = inputJunction.getElementsByTagName("position").item(0).getFirstChild().getNodeValue(); junctionPosition.put(junctionPos, cityId); String jName = cityMap.get(junctionPos); if (Util.hasValue(jName)) { outputJunction.setAttribute("position", jName); } else { throw new ConfigurationException("Unknown position: " + junctionPos); } } private void convertConnection(Element inputConnection, Element outputTile) throws ConfigurationException { String type = inputConnection.getElementsByTagName("conType").item(0).getFirstChild().getNodeValue(); String gauge = gaugeMap.get(type); Element outputConnection; if (gauge == null) { throw new ConfigurationException("Unknown gauge type: " + type); } else { outputConnection = outputDoc.createElement("Track"); outputConnection.setAttribute("gauge", gauge); } // 1841 special: A pass is not a track type but a station type. if (type.equals("ctMountain")) outputJunction.setAttribute("type", "pass"); boolean fromOK = convertTrackEnd(inputConnection, outputConnection, "position1", "from"); boolean toOK = convertTrackEnd(inputConnection, outputConnection, "position2", "to"); if (fromOK && toOK) outputTile.appendChild(outputConnection); } private boolean convertTrackEnd(Element inputConnection, Element outputConnection, String inputName, String outputName) throws ConfigurationException { String position = inputConnection.getElementsByTagName(inputName).item(0).getFirstChild().getNodeValue(); String end = sidesMap.get(position); if (end == null) end = junctionPosition.get(position); if (end != null) { outputConnection.setAttribute(outputName, end); resolvedTrack.put(outputConnection, end); return true; } else { if (!unresolvedTrack.containsKey(position)) { unresolvedTrack.put(position, new ArrayList<Element>()); } unresolvedTrack.get(position).add(outputConnection); return false; } } } --- NEW FILE: MakeGameTileSets.java --- package tools; import java.io.*; import java.util.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.*; import rails.game.ConfigurationException; import tools.XmlUtils; /** * Convert an XML tile dictionary, as created by Marco Rocci's Tile Designer, to * an XML file for use in Rails 18xx. */ public class MakeGameTileSets { private static String tilesFilePath = "tiles/Tiles.xml"; public static void main(String[] args) { try { if (args.length == 0) { System.out.println("Provide rails.game name(s) for which to create" + " tile sets as argument(s).\nALL implies all games below the data directory."); } else if (args[0].equalsIgnoreCase("ALL")) { List<String> games = new ArrayList<String>(); File gamesDir = new File("data"); if (gamesDir.exists() && gamesDir.isDirectory()) { File[] files = gamesDir.listFiles(); for (int i = 0; i < files.length; i++) { if (files[i].isDirectory() && !files[i].getName().equalsIgnoreCase("CVS")) { games.add(files[i].getName()); } } } new MakeGameTileSets(games.toArray(new String[0])); } else { new MakeGameTileSets(args); } } catch (ConfigurationException e) { e.printStackTrace(); } } private MakeGameTileSets(String[] games) throws ConfigurationException { Element inputTopElement = XmlUtils.findElementInFile(tilesFilePath, "Tiles"); Map<String, Element> tileMap = new HashMap<String, Element>(); Element tileSpec; String tileName; NodeList tList = inputTopElement.getElementsByTagName("Tile"); for (int i = 0; i < tList.getLength(); i++) { tileSpec = (Element) tList.item(i); tileName = tileSpec.getAttribute("id"); tileMap.put(tileName, tileSpec); } for (int i = 0; i < games.length; i++) { makeTileSet(games[i], tileMap); } } private void makeTileSet(String gameName, Map<String, Element> tileMap) throws ConfigurationException { // Open and read the tile set for this rails.game String tileSetPath = "data/" + gameName + "/TileSet.xml"; Element tileSet = XmlUtils.findElementInFile(tileSetPath, "TileManager"); if (tileSet == null) return; NodeList tiles = tileSet.getElementsByTagName("Tile"); Map<String, Object> tilesInSet = new HashMap<String, Object>(); // Also open and read the map tiles. String mapPath = "data/" + gameName + "/Map.xml"; Element mapHexes = XmlUtils.findElementInFile(mapPath, "Map"); NodeList hexes = mapHexes.getElementsByTagName("Hex"); String tilesPath = "data/" + gameName + "/Tiles.xml"; Document outputDoc; String tileName; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); DOMImplementation impl = builder.getDOMImplementation(); outputDoc = impl.createDocument(null, "Tiles", null); // Scan the TileSet for (int i = 0; i < tiles.getLength(); i++) { tileName = ((Element) tiles.item(i)).getAttribute("id"); // Save the tile in a Map so that we can check completeness // later. tilesInSet.put(tileName, null); // Get the Tile specification Element tileSpec = tileMap.get(tileName); if (tileSpec != null) { // Copy it to the subset document Element copy = (Element) outputDoc.importNode( (tileMap.get(tileName)), true); outputDoc.getDocumentElement().appendChild(copy); } else { System.out.println("ERROR: specified " + gameName + " tile " + tileName + " not found in Tiles.xml."); } } // Scan the map, and add any missing tiles, with a warning. for (int i = 0; i < hexes.getLength(); i++) { tileName = ((Element) hexes.item(i)).getAttribute("tile"); // Does the preprinted tile occur in TileSet? if (tilesInSet.containsKey(tileName)) continue; // No, warn and add it to the tiles document. System.out.println("WARNING: " + gameName + " preprinted map tile " + tileName + " does not occur in TileSet!"); // Get the Tile specification // Element tileSpec = (Element) tileMap.get(tileName); // Copy it to the subset document Element copy = (Element) outputDoc.importNode( (tileMap.get(tileName)), true); outputDoc.getDocumentElement().appendChild(copy); } TransformerFactory.newInstance().newTransformer().transform( new DOMSource(outputDoc), new StreamResult(new FileOutputStream(new File(tilesPath)))); } catch (Exception e) { throw new ConfigurationException("Document build error", e); } } } |