001/* 002 * XGSSGeomReader -- A class that can read and write an XGSS formatted geometry file. 003 * 004 * Copyright (C) 2013-2016, Joseph A. Huwaldt 005 * All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 * Or visit: http://www.gnu.org/licenses/lgpl.html 021 */ 022package geomss.geom.reader; 023 024import geomss.geom.GeomElement; 025import geomss.geom.GeomList; 026import geomss.geom.GeomXMLBinding; 027import geomss.geom.GeometryList; 028import java.io.File; 029import java.io.FileInputStream; 030import java.io.FileOutputStream; 031import java.io.IOException; 032import java.text.MessageFormat; 033import java.util.Locale; 034import java.util.Map; 035import static java.util.Objects.nonNull; 036import static java.util.Objects.requireNonNull; 037import java.util.zip.GZIPInputStream; 038import java.util.zip.GZIPOutputStream; 039import java.util.zip.ZipException; 040import javolution.context.StackContext; 041import javolution.util.FastMap; 042import javolution.xml.XMLObjectReader; 043import javolution.xml.XMLObjectWriter; 044import javolution.xml.XMLReferenceResolver; 045import javolution.xml.stream.XMLStreamException; 046 047/** 048 * A {@link GeomReader} for reading geometry from an XGSS formatted, GZIP compressed, XML 049 * geometry file. XGSS is the native file format for GeomSS. 050 * 051 * <p> Modified by: Joseph A. Huwaldt </p> 052 * 053 * @author Joseph A. Huwaldt, Date: June 20, 2013 054 * @version September 9, 2016 055 */ 056public class XGSSGeomReader extends AbstractGeomReader { 057 058 // A brief description of the data read by this reaader. 059 private static final String DESCRIPTION = RESOURCES.getString("xgssDescription"); 060 061 /** 062 * The preferred file extension for files of this reader's type. 063 */ 064 public static final String EXTENSION = "xgss"; 065 066 /** 067 * The XML file tag used for all GeomElement objects. 068 */ 069 private static final String GEOMETRYTAG = "Geometry"; 070 071 /** 072 * The XML file tag used for all non-geometry related workspace items. 073 */ 074 private static final String WORKSPACETAG = "Workspace"; 075 076 /** 077 * The user data key used to store the variable name for geometry stored in the 078 * workspace. 079 */ 080 private static final String VARNAMEKEY = "GeomSSVarName"; 081 082 /** 083 * The XML binding to use in reading/writing an XGSS formatted file. 084 */ 085 private static final GeomXMLBinding BINDING = new GeomXMLBinding(); 086 087 /** 088 * Returns a string representation of the object. This will return a brief description 089 * of the format read by this reader. 090 * 091 * @return A brief description of the format read by this reader. 092 */ 093 @Override 094 public String toString() { 095 return DESCRIPTION; 096 } 097 098 /** 099 * Returns the preferred file extension (not including the ".") for files of this 100 * GeomReader's type. 101 * 102 * @return The preferred file extension for files of this readers type. 103 */ 104 @Override 105 public String getExtension() { 106 return EXTENSION; 107 } 108 109 /** 110 * This method always returns <code>true</code> as XGSS files do encode the units that 111 * are being used for each geometry element. 112 * 113 * @return This method always returns true. 114 */ 115 @Override 116 public boolean isUnitAware() { 117 return true; 118 } 119 120 /** 121 * Method that determines if this reader can read geometry from the specified input 122 * file. 123 * 124 * @param inputFile The input file containing the geometry to be read in. 125 * @return GeomReader.NO if the file format is not recognized by this reader. 126 * GeomReader.YES if the file format is definitely recognized by this reader. 127 * GeomReader.MAYBE if the file format might be readable by this reader, but 128 * that can't easily be determined without actually reading the file. 129 * @throws java.io.IOException If there is a problem reading from the specified 130 * file. 131 */ 132 @Override 133 public int canReadData(File inputFile) throws IOException { 134 135 int response = NO; 136 if (inputFile.isFile() && inputFile.exists()) { 137 String name = inputFile.getName(); 138 name = name.toLowerCase().trim(); 139 if (name.endsWith(".xgss")) 140 response = YES; 141 } 142 143 return response; 144 } 145 146 /** 147 * Reads in an XGSS geometry file from the specified input file and returns a 148 * {@link GeometryList} object that contains the geometry from the file. 149 * 150 * @param inputFile The input file containing the geometry to be read in. May not be 151 * null. 152 * @return A {@link GeometryList} object containing the geometry read in from the 153 * file. If the file has no readable geometry in it, then this list will have 154 * no elements in it (will have a size() of 0). 155 * @throws IOException If there is a problem reading the specified file. 156 */ 157 @Override 158 public GeometryList read(File inputFile) throws IOException { 159 requireNonNull(inputFile); 160 161 // XGSS XML files are required to be in ASCII with U.S. style number formatting. 162 // Get the default locale and save it. Then change the default locale to US. 163 Locale defLocale = Locale.getDefault(); 164 Locale.setDefault(Locale.US); 165 166 try (GZIPInputStream is = new GZIPInputStream(new FileInputStream(inputFile))) { 167 168 // Create an XML object reader. 169 XMLObjectReader reader = XMLObjectReader.newInstance(is); 170 171 // Set the GeomSS XML binding to get consistant formatting. 172 reader.setBinding(BINDING); 173 174 // Use a reference resolver. 175 reader.setReferenceResolver(new XMLReferenceResolver()); 176 177 // Read in the XML file and return a top level Geometry List. 178 GeometryList<?, GeomElement> geom = reader.read(GEOMETRYTAG); 179 180 // For this version of "read" strip out any workspace variable name references 181 // in the geometry. 182 for (GeomElement elem : geom) { 183 elem.removeUserData(VARNAMEKEY); 184 } 185 186 return geom; 187 188 } catch (ZipException e) { 189 throw new IOException(MessageFormat.format( 190 RESOURCES.getString("xgssWrongFormat"), inputFile.getName()), e); 191 192 } catch (XMLStreamException e) { 193 throw new IOException(e); 194 195 } finally { 196 // Restore the default locale. 197 Locale.setDefault(defLocale); 198 } 199 } 200 201 /** 202 * Returns <code>true</code>. This reader can write all geometry element types to an 203 * XGSS file. 204 * 205 * @return This method always returns true. 206 */ 207 @Override 208 public boolean canWriteData() { 209 return true; 210 } 211 212 /** 213 * Writes out a geometry file for the geometry contained in the supplied 214 * {@link GeometryList} object. 215 * 216 * @param outputFile The output File to which the geometry is to be written. May not 217 * be null. 218 * @param geometry The {@link GeometryList} object containing the geometry to be 219 * written out. May not be null. 220 * @throws IOException If there is a problem writing to the specified file. 221 */ 222 @Override 223 public void write(File outputFile, GeometryList geometry) throws IOException { 224 requireNonNull(outputFile); 225 _warnings.clear(); 226 if (!geometry.containsGeometry()) { 227 _warnings.add(RESOURCES.getString("noGeometryWarning")); 228 return; 229 } 230 231 // XGSS XML files are required to be in ASCII with U.S. style number formatting. 232 // Get the default locale and save it. Then change the default locale to US. 233 Locale defLocale = Locale.getDefault(); 234 Locale.setDefault(Locale.US); 235 236 StackContext.enter(); 237 try (GZIPOutputStream os = new GZIPOutputStream(new FileOutputStream(outputFile))) { 238 // Create an XML object writer. 239 XMLObjectWriter writer = XMLObjectWriter.newInstance(os); 240 241 // Set the GeomSS XML binding to get consistant formatting. Use a tabs for spacing. 242 writer.setIndentation("\t"); 243 writer.setBinding(BINDING); 244 245 // Use a reference resolver to prevent duplicate objects in the file. 246 writer.setReferenceResolver(new XMLReferenceResolver()); 247 248 // Write out the top level geometry list to the file. 249 writer.write(geometry, GEOMETRYTAG); 250 251 writer.close(); 252 253 } catch (XMLStreamException e) { 254 throw new IOException(e); 255 256 } finally { 257 StackContext.exit(); 258 259 // Restore the default locale. 260 Locale.setDefault(defLocale); 261 } 262 } 263 264 /** 265 * Writes out an XGSS geometry file for the and non-geometry workspace variables 266 * contained in the supplied maps. 267 * 268 * @param outputFile The output File to which the geometry is to be written. May not 269 * be null. 270 * @param geometry A map identifying geometry elements by their associated variable 271 * names. May not be null. 272 * @param vars A map identifying non-geometry related workspace variables by 273 * their variable names. May not be null. 274 * @throws IOException If there is a problem writing to the specified file. 275 */ 276 public void write(File outputFile, Map<String, GeomElement> geometry, Map<String, Object> vars) throws IOException { 277 requireNonNull(outputFile); 278 requireNonNull(vars); 279 _warnings.clear(); 280 if (geometry.isEmpty()) { 281 _warnings.add(RESOURCES.getString("noGeometryWarning")); 282 } 283 284 StackContext.enter(); 285 try (GZIPOutputStream os = new GZIPOutputStream(new FileOutputStream(outputFile))) { 286 // Convert the geometry map into a list of geometry with the variable names stored 287 // in the user data. 288 GeomList geom = GeomList.newInstance(); 289 for (String varName : geometry.keySet()) { 290 GeomElement elem = geometry.get(varName); 291 elem.putUserData(VARNAMEKEY, varName); 292 geom.add(elem); 293 } 294 295 // Create an XML object writer. 296 XMLObjectWriter writer = XMLObjectWriter.newInstance(os); 297 298 // Set the GeomSS XML binding to get consistant formatting. Use a tabs for spacing. 299 writer.setIndentation("\t"); 300 writer.setBinding(BINDING); 301 302 // Use a reference resolver to prevent duplicate objects in the file. 303 writer.setReferenceResolver(new XMLReferenceResolver()); 304 305 // Write out the top level geometry list to the file. 306 writer.write(geom, GEOMETRYTAG); 307 308 // Write the Map of workspace variables to the file. 309 if (!vars.isEmpty()) 310 writer.write(vars, WORKSPACETAG); 311 312 writer.close(); 313 314 } catch (XMLStreamException e) { 315 throw new IOException(e); 316 317 } finally { 318 // Remove the variable names added to the user data above. 319 for (String varName : geometry.keySet()) { 320 GeomElement elem = geometry.get(varName); 321 elem.removeUserData(VARNAMEKEY); 322 } 323 StackContext.exit(); 324 } 325 } 326 327 /** 328 * Reads in an XGSS geometry + workspace file from the specified input file and 329 * returns a Map object that contains the geometry and workspace variables from the 330 * file with the variable names as the keys. 331 * 332 * @param inputFile The input file containing the geometry + workspace to be read in. 333 * @return A Map object containing the geometry and workspace variables read in from 334 * the file with the variable names as the keys. If the file has no readable 335 * geometry in it, then this map will have no contents. 336 * @throws IOException If there is a problem reading the specified file. 337 */ 338 public Map<String, Object> readWorkspace(File inputFile) throws IOException { 339 340 try (GZIPInputStream is = new GZIPInputStream(new FileInputStream(inputFile))) { 341 // Create an XML object reader. 342 XMLObjectReader reader = XMLObjectReader.newInstance(is); 343 344 // Set the GeomSS XML binding to get consistant formatting. 345 reader.setBinding(BINDING); 346 347 // Use a reference resolver. 348 reader.setReferenceResolver(new XMLReferenceResolver()); 349 350 // Read in the geometry and return a top level Geometry List. 351 GeometryList<?, GeomElement> geom = reader.read(GEOMETRYTAG); 352 353 // Read in the optional non-geometry workspace variables. 354 Map<String, Object> vars = null; 355 try { 356 vars = reader.read(WORKSPACETAG); 357 } catch (XMLStreamException e) { /* ignore (means workspace is not present) */ } 358 359 // Close the reader. 360 reader.close(); 361 362 // For this version of "read" add named elements to the workspace by their 363 // variable name. 364 FastMap<String, Object> workspace = FastMap.newInstance(); 365 for (GeomElement elem : geom) { 366 String varName = (String)elem.getUserData(VARNAMEKEY); 367 if (nonNull(varName)) 368 workspace.put(varName, elem); 369 } 370 371 // Add any non-geometry variables to the output workspace. 372 if (nonNull(vars) && !vars.isEmpty()) 373 workspace.putAll(vars); 374 375 // If there were no named variable names defined, put in the entire geometry 376 // list with the name "geom". 377 if (workspace.isEmpty()) 378 workspace.put("geom", geom); 379 380 return (Map<String, Object>)workspace; 381 382 } catch (ZipException e) { 383 throw new IOException(MessageFormat.format( 384 RESOURCES.getString("xgssWrongFormat"), inputFile.getName()), e); 385 386 } catch (XMLStreamException e) { 387 throw new IOException(e); 388 } 389 } 390 391}