1 /* 2 * HomeXMLExporter.java 3 * 4 * Copyright (c) 2015 Emmanuel PUYBARET / eTeks <info@eteks.com>. All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 package com.eteks.sweethome3d.io; 21 22 import java.io.IOException; 23 import java.math.BigDecimal; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 30 import com.eteks.sweethome3d.model.BackgroundImage; 31 import com.eteks.sweethome3d.model.Baseboard; 32 import com.eteks.sweethome3d.model.Camera; 33 import com.eteks.sweethome3d.model.Compass; 34 import com.eteks.sweethome3d.model.Content; 35 import com.eteks.sweethome3d.model.DimensionLine; 36 import com.eteks.sweethome3d.model.Home; 37 import com.eteks.sweethome3d.model.HomeDoorOrWindow; 38 import com.eteks.sweethome3d.model.HomeEnvironment; 39 import com.eteks.sweethome3d.model.HomeFurnitureGroup; 40 import com.eteks.sweethome3d.model.HomeLight; 41 import com.eteks.sweethome3d.model.HomeMaterial; 42 import com.eteks.sweethome3d.model.HomeObject; 43 import com.eteks.sweethome3d.model.HomePieceOfFurniture; 44 import com.eteks.sweethome3d.model.HomePrint; 45 import com.eteks.sweethome3d.model.HomeTexture; 46 import com.eteks.sweethome3d.model.Label; 47 import com.eteks.sweethome3d.model.Level; 48 import com.eteks.sweethome3d.model.LightSource; 49 import com.eteks.sweethome3d.model.ObserverCamera; 50 import com.eteks.sweethome3d.model.Polyline; 51 import com.eteks.sweethome3d.model.Room; 52 import com.eteks.sweethome3d.model.Sash; 53 import com.eteks.sweethome3d.model.TextStyle; 54 import com.eteks.sweethome3d.model.Transformation; 55 import com.eteks.sweethome3d.model.Wall; 56 import com.eteks.sweethome3d.tools.URLContent; 57 58 /** 59 * Exporter for home instances. Homes will be written using the DTD given in {@link HomeXMLHandler} class. 60 * @author Emmanuel Puybaret 61 */ 62 public class HomeXMLExporter extends ObjectXMLExporter<Home> { 63 private Map<Content, String> savedContentNames; 64 private Map<Level, String> levelIds = new HashMap<Level, String>(); 65 private Map<Wall, String> wallIds = new HashMap<Wall, String>(); 66 67 /** 68 * Sets the names that will be saved as XML attribute values for each content. 69 */ setSavedContentNames(Map<Content, String> savedContentNames)70 void setSavedContentNames(Map<Content, String> savedContentNames) { 71 this.savedContentNames = savedContentNames; 72 } 73 74 /** 75 * Returns the XML id of the given <code>object</code> that can be referenced by other elements. 76 * @throws IllegalArgumentException if the <code>object</code> has no associated id. 77 */ getId(Object object)78 protected String getId(Object object) { 79 if (object == null) { 80 return null; 81 } else if (object instanceof Level) { 82 return this.levelIds.get(object); 83 } else if (object instanceof Wall) { 84 return this.wallIds.get(object); 85 } else { 86 throw new IllegalArgumentException("No Id provided for object of class " + object.getClass().getName()); 87 } 88 } 89 90 /** 91 * Writes in XML the <code>home</code> object and the objects that depends on it with the given <code>writer</code>. 92 */ 93 @Override writeElement(XMLWriter writer, Home home)94 public void writeElement(XMLWriter writer, Home home) throws IOException { 95 // Store level ids 96 for (Level level : home.getLevels()) { 97 this.levelIds.put(level, level.getId()); 98 } 99 // Store wall ids 100 for (Wall wall : home.getWalls()) { 101 this.wallIds.put(wall, wall.getId()); 102 } 103 super.writeElement(writer, home); 104 } 105 106 /** 107 * Writes as XML attributes some data of <code>home</code> object with the given <code>writer</code>. 108 */ 109 @Override writeAttributes(XMLWriter writer, Home home)110 protected void writeAttributes(XMLWriter writer, Home home) throws IOException { 111 home.setVersion(Home.CURRENT_VERSION); 112 writer.writeAttribute("version", String.valueOf(home.getVersion())); 113 writer.writeAttribute("name", home.getName(), null); 114 writer.writeAttribute("camera", home.getCamera() == home.getObserverCamera() ? "observerCamera" : "topCamera"); 115 writer.writeAttribute("selectedLevel", getId(home.getSelectedLevel()), null); 116 writer.writeFloatAttribute("wallHeight", home.getWallHeight()); 117 writer.writeBooleanAttribute("basePlanLocked", home.isBasePlanLocked(), false); 118 if (home.getFurnitureSortedProperty() != null) { 119 writer.writeAttribute("furnitureSortedProperty", home.getFurnitureSortedProperty().name()); 120 } 121 writer.writeBooleanAttribute("furnitureDescendingSorted", home.isFurnitureDescendingSorted(), false); 122 } 123 124 /** 125 * Writes as XML elements some objects that depends on of <code>home</code> with the given <code>writer</code>. 126 */ 127 @Override writeChildren(XMLWriter writer, Home home)128 protected void writeChildren(XMLWriter writer, Home home) throws IOException { 129 // Write properties in the alphabetic order of their names 130 List<String> propertiesNames = new ArrayList<String>(home.getPropertyNames()); 131 Collections.sort(propertiesNames); 132 for (String propertyName : propertiesNames) { 133 writeProperty(writer, propertyName, home.getProperty(propertyName)); 134 } 135 // Write furniture visible properties 136 for (HomePieceOfFurniture.SortableProperty property : home.getFurnitureVisibleProperties()) { 137 writer.writeStartElement("furnitureVisibleProperty"); 138 writer.writeAttribute("name", property.name()); 139 writer.writeEndElement(); 140 } 141 // Write environment, compass and cameras 142 writeEnvironment(writer, home.getEnvironment()); 143 writeBackgroundImage(writer, home.getBackgroundImage()); 144 writePrint(writer, home.getPrint()); 145 writeCompass(writer, home.getCompass()); 146 writeCamera(writer, home.getObserverCamera(), "observerCamera"); 147 writeCamera(writer, home.getTopCamera(), "topCamera"); 148 for (Camera camera : home.getStoredCameras()) { 149 writeCamera(writer, camera, "storedCamera"); 150 } 151 // Write Level elements 152 for (Level level : home.getLevels()) { 153 writeLevel(writer, level); 154 } 155 // Write furniture and other home elements 156 for (HomePieceOfFurniture piece : home.getFurniture()) { 157 writePieceOfFurniture(writer, piece); 158 } 159 for (Wall wall : home.getWalls()) { 160 writeWall(writer, wall); 161 } 162 for (Room room : home.getRooms()) { 163 writeRoom(writer, room); 164 } 165 for (Polyline polyline : home.getPolylines()) { 166 writePolyline(writer, polyline); 167 } 168 for (DimensionLine dimensionLine : home.getDimensionLines()) { 169 writeDimensionLine(writer, dimensionLine); 170 } 171 for (Label label : home.getLabels()) { 172 writeLabel(writer, label); 173 } 174 } 175 176 /** 177 * Writes in XML the <code>environment</code> object with the given <code>writer</code>. 178 */ writeEnvironment(XMLWriter writer, HomeEnvironment environment)179 protected void writeEnvironment(XMLWriter writer, HomeEnvironment environment) throws IOException { 180 new ObjectXMLExporter<HomeEnvironment>() { 181 @Override 182 protected void writeAttributes(XMLWriter writer, HomeEnvironment environment) throws IOException { 183 writer.writeColorAttribute("groundColor", environment.getGroundColor()); 184 writer.writeBooleanAttribute("backgroundImageVisibleOnGround3D", environment.isBackgroundImageVisibleOnGround3D(), false); 185 writer.writeColorAttribute("skyColor", environment.getSkyColor()); 186 writer.writeColorAttribute("lightColor", environment.getLightColor()); 187 writer.writeFloatAttribute("wallsAlpha", environment.getWallsAlpha(), 0); 188 writer.writeBooleanAttribute("allLevelsVisible", environment.isAllLevelsVisible(), false); 189 writer.writeBooleanAttribute("observerCameraElevationAdjusted", environment.isObserverCameraElevationAdjusted(), true); 190 writer.writeColorAttribute("ceillingLightColor", environment.getCeillingLightColor()); 191 writer.writeAttribute("drawingMode", environment.getDrawingMode().name(), HomeEnvironment.DrawingMode.FILL.name()); 192 writer.writeFloatAttribute("subpartSizeUnderLight", environment.getSubpartSizeUnderLight(), 0); 193 writer.writeIntegerAttribute("photoWidth", environment.getPhotoWidth()); 194 writer.writeIntegerAttribute("photoHeight", environment.getPhotoHeight()); 195 writer.writeAttribute("photoAspectRatio", environment.getPhotoAspectRatio().name()); 196 writer.writeIntegerAttribute("photoQuality", environment.getPhotoQuality()); 197 writer.writeIntegerAttribute("videoWidth", environment.getVideoWidth()); 198 writer.writeAttribute("videoAspectRatio", environment.getVideoAspectRatio().name()); 199 writer.writeIntegerAttribute("videoQuality", environment.getVideoQuality()); 200 writer.writeFloatAttribute("videoSpeed", environment.getVideoSpeed(), 2400f / 3600); 201 writer.writeIntegerAttribute("videoFrameRate", environment.getVideoFrameRate()); 202 } 203 204 @Override 205 protected void writeChildren(XMLWriter writer, HomeEnvironment environment) throws IOException { 206 writeProperties(writer, environment); 207 if (!environment.getVideoCameraPath().isEmpty()) { 208 for (Camera camera : environment.getVideoCameraPath()) { 209 writeCamera(writer, camera, "cameraPath"); 210 } 211 } 212 writeTexture(writer, environment.getGroundTexture(), "groundTexture"); 213 writeTexture(writer, environment.getSkyTexture(), "skyTexture"); 214 } 215 }.writeElement(writer, environment); 216 } 217 218 /** 219 * Writes in XML the <code>background</code> object with the given <code>writer</code>. 220 */ writeBackgroundImage(XMLWriter writer, BackgroundImage backgroundImage)221 protected void writeBackgroundImage(XMLWriter writer, BackgroundImage backgroundImage) throws IOException { 222 if (backgroundImage != null) { 223 new ObjectXMLExporter<BackgroundImage>() { 224 @Override 225 protected void writeAttributes(XMLWriter writer, BackgroundImage backgroundImage) throws IOException { 226 writer.writeAttribute("image", getExportedContentName(backgroundImage, backgroundImage.getImage()), null); 227 writer.writeFloatAttribute("scaleDistance", backgroundImage.getScaleDistance()); 228 writer.writeFloatAttribute("scaleDistanceXStart", backgroundImage.getScaleDistanceXStart()); 229 writer.writeFloatAttribute("scaleDistanceYStart", backgroundImage.getScaleDistanceYStart()); 230 writer.writeFloatAttribute("scaleDistanceXEnd", backgroundImage.getScaleDistanceXEnd()); 231 writer.writeFloatAttribute("scaleDistanceYEnd", backgroundImage.getScaleDistanceYEnd()); 232 writer.writeFloatAttribute("xOrigin", backgroundImage.getXOrigin(), 0); 233 writer.writeFloatAttribute("yOrigin", backgroundImage.getYOrigin(), 0); 234 writer.writeBooleanAttribute("visible", backgroundImage.isVisible(), true); 235 } 236 }.writeElement(writer, backgroundImage); 237 } 238 } 239 240 /** 241 * Writes in XML the <code>print</code> object with the given <code>writer</code>. 242 */ writePrint(XMLWriter writer, HomePrint print)243 protected void writePrint(XMLWriter writer, HomePrint print) throws IOException { 244 if (print != null) { 245 new ObjectXMLExporter<HomePrint>() { 246 @Override 247 protected void writeAttributes(XMLWriter writer, HomePrint print) throws IOException { 248 writer.writeAttribute("headerFormat", print.getHeaderFormat(), null); 249 writer.writeAttribute("footerFormat", print.getFooterFormat(), null); 250 writer.writeBooleanAttribute("furniturePrinted", print.isFurniturePrinted(), true); 251 writer.writeBooleanAttribute("planPrinted", print.isPlanPrinted(), true); 252 writer.writeBooleanAttribute("view3DPrinted", print.isView3DPrinted(), true); 253 writer.writeFloatAttribute("planScale", print.getPlanScale()); 254 writer.writeFloatAttribute("paperWidth", print.getPaperWidth()); 255 writer.writeFloatAttribute("paperHeight", print.getPaperHeight()); 256 writer.writeFloatAttribute("paperTopMargin", print.getPaperTopMargin()); 257 writer.writeFloatAttribute("paperLeftMargin", print.getPaperLeftMargin()); 258 writer.writeFloatAttribute("paperBottomMargin", print.getPaperBottomMargin()); 259 writer.writeFloatAttribute("paperRightMargin", print.getPaperRightMargin()); 260 writer.writeAttribute("paperOrientation", print.getPaperOrientation().name()); 261 } 262 }.writeElement(writer, print); 263 } 264 } 265 266 /** 267 * Writes in XML the <code>compass</code> object with the given <code>writer</code>. 268 */ writeCompass(XMLWriter writer, Compass compass)269 protected void writeCompass(XMLWriter writer, Compass compass) throws IOException { 270 new ObjectXMLExporter<Compass>() { 271 @Override 272 protected void writeAttributes(XMLWriter writer, Compass compass) throws IOException { 273 writer.writeFloatAttribute("x", compass.getX()); 274 writer.writeFloatAttribute("y", compass.getY()); 275 writer.writeFloatAttribute("diameter", compass.getDiameter()); 276 writer.writeFloatAttribute("northDirection", compass.getNorthDirection()); 277 writer.writeFloatAttribute("longitude", compass.getLongitude()); 278 writer.writeFloatAttribute("latitude", compass.getLatitude()); 279 writer.writeAttribute("timeZone", compass.getTimeZone()); 280 writer.writeBooleanAttribute("visible", compass.isVisible(), true); 281 } 282 283 @Override 284 protected void writeChildren(XMLWriter writer, Compass compass) throws IOException { 285 writeProperties(writer, compass); 286 } 287 }.writeElement(writer, compass); 288 } 289 290 /** 291 * Writes in XML the <code>camera</code> object with the given <code>writer</code>. 292 */ writeCamera(XMLWriter writer, Camera camera, final String attributeName)293 protected void writeCamera(XMLWriter writer, Camera camera, final String attributeName) throws IOException { 294 if (camera != null) { 295 new ObjectXMLExporter<Camera>() { 296 @Override 297 protected void writeAttributes(XMLWriter writer, Camera camera) throws IOException { 298 writer.writeAttribute("attribute", attributeName, null); 299 if (!"observerCamera".equals(attributeName) 300 && !"topCamera".equals(attributeName)) { 301 writer.writeAttribute("id", camera.getId()); 302 } 303 writer.writeAttribute("name", camera.getName(), null); 304 writer.writeAttribute("lens", camera.getLens().name()); 305 writer.writeFloatAttribute("x", camera.getX()); 306 writer.writeFloatAttribute("y", camera.getY()); 307 writer.writeFloatAttribute("z", camera.getZ()); 308 writer.writeFloatAttribute("yaw", camera.getYaw()); 309 writer.writeFloatAttribute("pitch", camera.getPitch()); 310 writer.writeFloatAttribute("fieldOfView", camera.getFieldOfView()); 311 writer.writeLongAttribute("time", camera.getTime()); 312 if (camera instanceof ObserverCamera) { 313 writer.writeBooleanAttribute("fixedSize", ((ObserverCamera)camera).isFixedSize(), false); 314 } 315 } 316 317 @Override 318 protected void writeChildren(XMLWriter writer, Camera camera) throws IOException { 319 writeProperties(writer, camera); 320 } 321 }.writeElement(writer, camera); 322 } 323 } 324 325 /** 326 * Writes in XML the <code>level</code> object with the given <code>writer</code>. 327 */ writeLevel(XMLWriter writer, Level level)328 protected void writeLevel(XMLWriter writer, Level level) throws IOException { 329 new ObjectXMLExporter<Level>() { 330 @Override 331 protected void writeAttributes(XMLWriter writer, Level level) throws IOException { 332 writer.writeAttribute("id", level.getId()); 333 writer.writeAttribute("name", level.getName()); 334 writer.writeFloatAttribute("elevation", level.getElevation()); 335 writer.writeFloatAttribute("floorThickness", level.getFloorThickness()); 336 writer.writeFloatAttribute("height", level.getHeight()); 337 writer.writeIntegerAttribute("elevationIndex", level.getElevationIndex()); 338 writer.writeBooleanAttribute("visible", level.isVisible(), true); 339 writer.writeBooleanAttribute("viewable", level.isViewable(), true); 340 } 341 342 @Override 343 protected void writeChildren(XMLWriter writer, Level level) throws IOException { 344 writeProperties(writer, level); 345 writeBackgroundImage(writer, level.getBackgroundImage()); 346 } 347 }.writeElement(writer, level); 348 } 349 350 /** 351 * Writes in XML the <code>piece</code> object with the given <code>writer</code>. 352 */ writePieceOfFurniture(XMLWriter writer, HomePieceOfFurniture piece)353 protected void writePieceOfFurniture(XMLWriter writer, HomePieceOfFurniture piece) throws IOException { 354 new PieceOfFurnitureExporter().writeElement(writer, piece); 355 } 356 357 /** 358 * Default exporter class used to write a piece of furniture in XML. 359 */ 360 protected class PieceOfFurnitureExporter extends ObjectXMLExporter<HomePieceOfFurniture> { PieceOfFurnitureExporter()361 public PieceOfFurnitureExporter() { 362 } 363 364 @Override writeAttributes(XMLWriter writer, HomePieceOfFurniture piece)365 protected void writeAttributes(XMLWriter writer, HomePieceOfFurniture piece) throws IOException { 366 writer.writeAttribute("id", piece.getId()); 367 if (piece.getLevel() != null) { 368 writer.writeAttribute("level", getId(piece.getLevel())); 369 } 370 writer.writeAttribute("catalogId", piece.getCatalogId(), null); 371 writer.writeAttribute("name", piece.getName()); 372 writer.writeAttribute("creator", piece.getCreator(), null); 373 writer.writeAttribute("model", getExportedContentName(piece, piece.getModel()), null); 374 writer.writeAttribute("icon", getExportedContentName(piece, piece.getIcon()), null); 375 writer.writeAttribute("planIcon", getExportedContentName(piece, piece.getPlanIcon()), null); 376 writer.writeFloatAttribute("x", piece.getX()); 377 writer.writeFloatAttribute("y", piece.getY()); 378 writer.writeFloatAttribute("elevation", piece.getElevation(), 0f); 379 writer.writeFloatAttribute("angle", piece.getAngle(), 0f); 380 writer.writeFloatAttribute("pitch", piece.getPitch(), 0f); 381 writer.writeFloatAttribute("roll", piece.getRoll(), 0f); 382 writer.writeFloatAttribute("width", piece.getWidth()); 383 writer.writeFloatAttribute("widthInPlan", piece.getWidthInPlan(), piece.getWidth()); 384 writer.writeFloatAttribute("depth", piece.getDepth()); 385 writer.writeFloatAttribute("depthInPlan", piece.getDepthInPlan(), piece.getDepth()); 386 writer.writeFloatAttribute("height", piece.getHeight()); 387 writer.writeFloatAttribute("heightInPlan", piece.getHeightInPlan(), piece.getHeight()); 388 writer.writeBooleanAttribute("backFaceShown", piece.isBackFaceShown(), false); 389 writer.writeBooleanAttribute("modelMirrored", piece.isModelMirrored(), false); 390 writer.writeBooleanAttribute("visible", piece.isVisible(), true); 391 writer.writeColorAttribute("color", piece.getColor()); 392 if (piece.getShininess() != null) { 393 writer.writeFloatAttribute("shininess", piece.getShininess()); 394 } 395 float [][] modelRotation = piece.getModelRotation(); 396 String modelRotationString = 397 floatToString(modelRotation[0][0]) + " " + floatToString(modelRotation[0][1]) + " " + floatToString(modelRotation[0][2]) + " " 398 + floatToString(modelRotation[1][0]) + " " + floatToString(modelRotation[1][1]) + " " + floatToString(modelRotation[1][2]) + " " 399 + floatToString(modelRotation[2][0]) + " " + floatToString(modelRotation[2][1]) + " " + floatToString(modelRotation[2][2]); 400 writer.writeAttribute("modelRotation", modelRotationString, "1 0 0 0 1 0 0 0 1"); 401 writer.writeBooleanAttribute("modelCenteredAtOrigin", piece.isModelCenteredAtOrigin(), true); 402 writer.writeLongAttribute("modelSize", piece.getModelSize()); 403 writer.writeAttribute("description", piece.getDescription(), null); 404 writer.writeAttribute("information", piece.getInformation(), null); 405 writer.writeBooleanAttribute("movable", piece.isMovable(), true); 406 if (!(piece instanceof HomeFurnitureGroup)) { 407 if (!(piece instanceof HomeDoorOrWindow)) { 408 writer.writeBooleanAttribute("doorOrWindow", piece.isDoorOrWindow(), false); 409 writer.writeBooleanAttribute("horizontallyRotatable", piece.isHorizontallyRotatable(), true); 410 } 411 writer.writeBooleanAttribute("resizable", piece.isResizable(), true); 412 writer.writeBooleanAttribute("deformable", piece.isDeformable(), true); 413 writer.writeBooleanAttribute("texturable", piece.isTexturable(), true); 414 } 415 if (piece instanceof HomeFurnitureGroup) { 416 BigDecimal price = piece.getPrice(); 417 // Ignore price of group if one of its children has a price 418 for (HomePieceOfFurniture groupPiece : ((HomeFurnitureGroup)piece).getFurniture()) { 419 if (groupPiece.getPrice() != null) { 420 price = null; 421 break; 422 } 423 } 424 writer.writeBigDecimalAttribute("price", price); 425 } else { 426 writer.writeBigDecimalAttribute("price", piece.getPrice()); 427 writer.writeBigDecimalAttribute("valueAddedTaxPercentage", piece.getValueAddedTaxPercentage()); 428 writer.writeAttribute("currency", piece.getCurrency(), null); 429 } 430 writer.writeAttribute("staircaseCutOutShape", piece.getStaircaseCutOutShape(), null); 431 writer.writeFloatAttribute("dropOnTopElevation", piece.getDropOnTopElevation(), 1f); 432 writer.writeBooleanAttribute("nameVisible", piece.isNameVisible(), false); 433 writer.writeFloatAttribute("nameAngle", piece.getNameAngle(), 0f); 434 writer.writeFloatAttribute("nameXOffset", piece.getNameXOffset(), 0f); 435 writer.writeFloatAttribute("nameYOffset", piece.getNameYOffset(), 0f); 436 if (piece instanceof HomeDoorOrWindow) { 437 HomeDoorOrWindow doorOrWindow = (HomeDoorOrWindow)piece; 438 writer.writeFloatAttribute("wallThickness", doorOrWindow.getWallThickness(), 1f); 439 writer.writeFloatAttribute("wallDistance", doorOrWindow.getWallDistance(), 0f); 440 writer.writeFloatAttribute("wallWidth", doorOrWindow.getWallWidth(), 1f); 441 writer.writeFloatAttribute("wallLeft", doorOrWindow.getWallLeft(), 0f); 442 writer.writeFloatAttribute("wallHeight", doorOrWindow.getWallHeight(), 1f); 443 writer.writeFloatAttribute("wallTop", doorOrWindow.getWallTop(), 0f); 444 writer.writeAttribute("cutOutShape", doorOrWindow.getCutOutShape(), null); 445 writer.writeBooleanAttribute("wallCutOutOnBothSides", doorOrWindow.isWallCutOutOnBothSides(), false); 446 writer.writeBooleanAttribute("widthDepthDeformable", doorOrWindow.isWidthDepthDeformable(), true); 447 writer.writeBooleanAttribute("boundToWall", doorOrWindow.isBoundToWall(), true); 448 } else if (piece instanceof HomeLight) { 449 writer.writeFloatAttribute("power", ((HomeLight)piece).getPower()); 450 } 451 } 452 453 @Override writeChildren(XMLWriter writer, HomePieceOfFurniture piece)454 protected void writeChildren(XMLWriter writer, HomePieceOfFurniture piece) throws IOException { 455 // Write subclass child elements 456 if (piece instanceof HomeFurnitureGroup) { 457 for (HomePieceOfFurniture groupPiece : ((HomeFurnitureGroup)piece).getFurniture()) { 458 writePieceOfFurniture(writer, groupPiece); 459 } 460 } else if (piece instanceof HomeLight) { 461 for (LightSource lightSource : ((HomeLight)piece).getLightSources()) { 462 writer.writeStartElement("lightSource"); 463 writer.writeFloatAttribute("x", lightSource.getX()); 464 writer.writeFloatAttribute("y", lightSource.getY()); 465 writer.writeFloatAttribute("z", lightSource.getZ()); 466 writer.writeColorAttribute("color", lightSource.getColor()); 467 writer.writeFloatAttribute("diameter", lightSource.getDiameter()); 468 writer.writeEndElement(); 469 } 470 } else if (piece instanceof HomeDoorOrWindow) { 471 for (Sash sash : ((HomeDoorOrWindow)piece).getSashes()) { 472 writer.writeStartElement("sash"); 473 writer.writeFloatAttribute("xAxis", sash.getXAxis()); 474 writer.writeFloatAttribute("yAxis", sash.getYAxis()); 475 writer.writeFloatAttribute("width", sash.getWidth()); 476 writer.writeFloatAttribute("startAngle", sash.getStartAngle()); 477 writer.writeFloatAttribute("endAngle", sash.getEndAngle()); 478 writer.writeEndElement(); 479 } 480 } 481 482 // Write child elements 483 writeProperties(writer, piece); 484 writeTextStyle(writer, piece.getNameStyle(), "nameStyle"); 485 writeTexture(writer, piece.getTexture(), null); 486 if (piece.getModelMaterials() != null) { 487 for (HomeMaterial material : piece.getModelMaterials()) { 488 writeMaterial(writer, material, piece.getModel()); 489 } 490 } 491 if (piece.getModelTransformations() != null) { 492 for (Transformation transformation : piece.getModelTransformations()) { 493 writer.writeStartElement("transformation"); 494 writer.writeAttribute("name", transformation.getName(), null); 495 float [][] matrix = transformation.getMatrix(); 496 String matrixString = 497 floatToString(matrix[0][0]) + " " + floatToString(matrix[0][1]) + " " + floatToString(matrix[0][2]) + " " + floatToString(matrix[0][3]) + " " 498 + floatToString(matrix[1][0]) + " " + floatToString(matrix[1][1]) + " " + floatToString(matrix[1][2]) + " " + floatToString(matrix[1][3]) + " " 499 + floatToString(matrix[2][0]) + " " + floatToString(matrix[2][1]) + " " + floatToString(matrix[2][2]) + " " + floatToString(matrix[2][3]); 500 writer.writeAttribute("matrix", matrixString); 501 writer.writeEndElement(); 502 } 503 } 504 } 505 } 506 507 /** 508 * Writes in XML the <code>material</code> object with the given <code>writer</code>. 509 */ writeMaterial(XMLWriter writer, HomeMaterial material, final Content model)510 protected void writeMaterial(XMLWriter writer, HomeMaterial material, final Content model) throws IOException { 511 if (material != null) { 512 new ObjectXMLExporter<HomeMaterial>() { 513 @Override 514 protected void writeAttributes(XMLWriter writer, HomeMaterial material) throws IOException { 515 writer.writeAttribute("name", material.getName()); 516 writer.writeAttribute("key", material.getKey(), null); 517 writer.writeColorAttribute("color", material.getColor()); 518 if (material.getShininess() != null) { 519 writer.writeFloatAttribute("shininess", material.getShininess()); 520 } 521 } 522 523 @Override 524 protected void writeChildren(XMLWriter writer, HomeMaterial material) throws IOException { 525 writeTexture(writer, material.getTexture(), null); 526 } 527 }.writeElement(writer, material); 528 } 529 } 530 531 /** 532 * Writes in XML the <code>wall</code> object with the given <code>writer</code>. 533 */ writeWall(XMLWriter writer, Wall wall)534 protected void writeWall(XMLWriter writer, Wall wall) throws IOException { 535 new ObjectXMLExporter<Wall>() { 536 @Override 537 protected void writeAttributes(XMLWriter writer, Wall wall) throws IOException { 538 writer.writeAttribute("id", wall.getId()); 539 if (wall.getLevel() != null) { 540 writer.writeAttribute("level", getId(wall.getLevel())); 541 } 542 if (wall.getWallAtStart() != null) { 543 String id = getId(wall.getWallAtStart()); 544 // Check id isn't null to ensure saved data consistency 545 if (id != null) { 546 writer.writeAttribute("wallAtStart", id); 547 } 548 } 549 if (wall.getWallAtEnd() != null) { 550 String id = getId(wall.getWallAtEnd()); 551 // Check id isn't null to ensure saved data consistency 552 if (id != null) { 553 writer.writeAttribute("wallAtEnd", id); 554 } 555 } 556 writer.writeFloatAttribute("xStart", wall.getXStart()); 557 writer.writeFloatAttribute("yStart", wall.getYStart()); 558 writer.writeFloatAttribute("xEnd", wall.getXEnd()); 559 writer.writeFloatAttribute("yEnd", wall.getYEnd()); 560 writer.writeFloatAttribute("height", wall.getHeight()); 561 writer.writeFloatAttribute("heightAtEnd", wall.getHeightAtEnd()); 562 writer.writeFloatAttribute("thickness", wall.getThickness()); 563 writer.writeFloatAttribute("arcExtent", wall.getArcExtent()); 564 if (wall.getPattern() != null) { 565 writer.writeAttribute("pattern", wall.getPattern().getName()); 566 } 567 writer.writeColorAttribute("topColor", wall.getTopColor()); 568 writer.writeColorAttribute("leftSideColor", wall.getLeftSideColor()); 569 writer.writeFloatAttribute("leftSideShininess", wall.getLeftSideShininess(), 0); 570 writer.writeColorAttribute("rightSideColor", wall.getRightSideColor()); 571 writer.writeFloatAttribute("rightSideShininess", wall.getRightSideShininess(), 0); 572 } 573 574 @Override 575 protected void writeChildren(XMLWriter writer, Wall wall) throws IOException { 576 writeProperties(writer, wall); 577 writeTexture(writer, wall.getLeftSideTexture(), "leftSideTexture"); 578 writeTexture(writer, wall.getRightSideTexture(), "rightSideTexture"); 579 writeBaseboard(writer, wall.getLeftSideBaseboard(), "leftSideBaseboard"); 580 writeBaseboard(writer, wall.getRightSideBaseboard(), "rightSideBaseboard"); 581 } 582 }.writeElement(writer, wall); 583 } 584 585 /** 586 * Writes in XML the <code>room</code> object with the given <code>writer</code>. 587 */ writeRoom(XMLWriter writer, Room room)588 protected void writeRoom(XMLWriter writer, Room room) throws IOException { 589 new ObjectXMLExporter<Room>() { 590 @Override 591 protected void writeAttributes(XMLWriter writer, Room room) throws IOException { 592 writer.writeAttribute("id", room.getId()); 593 if (room.getLevel() != null) { 594 writer.writeAttribute("level", getId(room.getLevel())); 595 } 596 writer.writeAttribute("name", room.getName(), null); 597 writer.writeFloatAttribute("nameAngle", room.getNameAngle(), 0f); 598 writer.writeFloatAttribute("nameXOffset", room.getNameXOffset(), 0f); 599 writer.writeFloatAttribute("nameYOffset", room.getNameYOffset(), -40f); 600 writer.writeBooleanAttribute("areaVisible", room.isAreaVisible(), false); 601 writer.writeFloatAttribute("areaAngle", room.getAreaAngle(), 0f); 602 writer.writeFloatAttribute("areaXOffset", room.getAreaXOffset(), 0f); 603 writer.writeFloatAttribute("areaYOffset", room.getAreaYOffset(), 0f); 604 writer.writeBooleanAttribute("floorVisible", room.isFloorVisible(), true); 605 writer.writeColorAttribute("floorColor", room.getFloorColor()); 606 writer.writeFloatAttribute("floorShininess", room.getFloorShininess(), 0); 607 writer.writeBooleanAttribute("ceilingVisible", room.isCeilingVisible(), true); 608 writer.writeColorAttribute("ceilingColor", room.getCeilingColor()); 609 writer.writeFloatAttribute("ceilingShininess", room.getCeilingShininess(), 0); 610 } 611 612 @Override 613 protected void writeChildren(XMLWriter writer, Room room) throws IOException { 614 writeProperties(writer, room); 615 writeTextStyle(writer, room.getNameStyle(), "nameStyle"); 616 writeTextStyle(writer, room.getAreaStyle(), "areaStyle"); 617 writeTexture(writer, room.getFloorTexture(), "floorTexture"); 618 writeTexture(writer, room.getCeilingTexture(), "ceilingTexture"); 619 for (float [] point : room.getPoints()) { 620 writer.writeStartElement("point"); 621 writer.writeFloatAttribute("x", point [0]); 622 writer.writeFloatAttribute("y", point [1]); 623 writer.writeEndElement(); 624 } 625 } 626 }.writeElement(writer, room); 627 } 628 629 /** 630 * Writes in XML the <code>polyline</code> object with the given <code>writer</code>. 631 */ writePolyline(XMLWriter writer, Polyline polyline)632 protected void writePolyline(XMLWriter writer, Polyline polyline) throws IOException { 633 new ObjectXMLExporter<Polyline>() { 634 @Override 635 protected void writeAttributes(XMLWriter writer, Polyline polyline) throws IOException { 636 writer.writeAttribute("id", polyline.getId()); 637 if (polyline.getLevel() != null) { 638 writer.writeAttribute("level", getId(polyline.getLevel())); 639 } 640 writer.writeFloatAttribute("thickness", polyline.getThickness(), 1f); 641 writer.writeAttribute("capStyle", polyline.getCapStyle().name(), Polyline.CapStyle.BUTT.name()); 642 writer.writeAttribute("joinStyle", polyline.getJoinStyle().name(), Polyline.JoinStyle.MITER.name()); 643 writer.writeAttribute("dashStyle", polyline.getDashStyle().name(), Polyline.DashStyle.SOLID.name()); 644 if (polyline.getDashStyle() == Polyline.DashStyle.CUSTOMIZED) { 645 StringBuilder dashPattern = new StringBuilder(); 646 for (float dashPart : polyline.getDashPattern()) { 647 dashPattern.append(floatToString(dashPart)); 648 dashPattern.append(" "); 649 } 650 dashPattern.setLength(dashPattern.length() - 1); 651 writer.writeAttribute("dashPattern", dashPattern.toString()); 652 } 653 writer.writeFloatAttribute("dashOffset", polyline.getDashOffset(), 0f); 654 writer.writeAttribute("startArrowStyle", polyline.getStartArrowStyle().name(), Polyline.ArrowStyle.NONE.name()); 655 writer.writeAttribute("endArrowStyle", polyline.getEndArrowStyle().name(), Polyline.ArrowStyle.NONE.name()); 656 if (polyline.isVisibleIn3D()) { 657 writer.writeFloatAttribute("elevation", polyline.getElevation()); 658 } 659 writer.writeColorAttribute("color", polyline.getColor()); 660 writer.writeBooleanAttribute("closedPath", polyline.isClosedPath(), false); 661 } 662 663 @Override 664 protected void writeChildren(XMLWriter writer, Polyline polyline) throws IOException { 665 writeProperties(writer, polyline); 666 for (float [] point : polyline.getPoints()) { 667 writer.writeStartElement("point"); 668 writer.writeFloatAttribute("x", point [0]); 669 writer.writeFloatAttribute("y", point [1]); 670 writer.writeEndElement(); 671 } 672 } 673 }.writeElement(writer, polyline); 674 } 675 676 /** 677 * Writes in XML the <code>dimensionLine</code> object with the given <code>writer</code>. 678 */ writeDimensionLine(XMLWriter writer, DimensionLine dimensionLine)679 protected void writeDimensionLine(XMLWriter writer, DimensionLine dimensionLine) throws IOException { 680 new ObjectXMLExporter<DimensionLine>() { 681 @Override 682 protected void writeAttributes(XMLWriter writer, DimensionLine dimensionLine) throws IOException { 683 writer.writeAttribute("id", dimensionLine.getId()); 684 if (dimensionLine.getLevel() != null) { 685 writer.writeAttribute("level", getId(dimensionLine.getLevel())); 686 } 687 writer.writeFloatAttribute("xStart", dimensionLine.getXStart()); 688 writer.writeFloatAttribute("yStart", dimensionLine.getYStart()); 689 writer.writeFloatAttribute("xEnd", dimensionLine.getXEnd()); 690 writer.writeFloatAttribute("yEnd", dimensionLine.getYEnd()); 691 writer.writeFloatAttribute("offset", dimensionLine.getOffset()); 692 } 693 694 @Override 695 protected void writeChildren(XMLWriter writer, DimensionLine dimensionLine) throws IOException { 696 writeProperties(writer, dimensionLine); 697 writeTextStyle(writer, dimensionLine.getLengthStyle(), "lengthStyle"); 698 } 699 }.writeElement(writer, dimensionLine); 700 } 701 702 /** 703 * Writes in XML the <code>label</code> object with the given <code>writer</code>. 704 */ writeLabel(XMLWriter writer, Label label)705 protected void writeLabel(XMLWriter writer, Label label) throws IOException { 706 new ObjectXMLExporter<Label>() { 707 @Override 708 protected void writeAttributes(XMLWriter writer, Label label) throws IOException { 709 writer.writeAttribute("id", label.getId()); 710 if (label.getLevel() != null) { 711 writer.writeAttribute("level", getId(label.getLevel())); 712 } 713 writer.writeFloatAttribute("x", label.getX()); 714 writer.writeFloatAttribute("y", label.getY()); 715 writer.writeFloatAttribute("angle", label.getAngle(), 0); 716 writer.writeFloatAttribute("elevation", label.getElevation(), 0); 717 writer.writeFloatAttribute("pitch", label.getPitch()); 718 writer.writeColorAttribute("color", label.getColor()); 719 writer.writeColorAttribute("outlineColor", label.getOutlineColor()); 720 } 721 722 @Override 723 protected void writeChildren(XMLWriter writer, Label label) throws IOException { 724 writeProperties(writer, label); 725 writeTextStyle(writer, label.getStyle(), null); 726 // Write text in a child element 727 writer.writeStartElement("text"); 728 writer.writeText(label.getText()); 729 writer.writeEndElement(); 730 } 731 }.writeElement(writer, label); 732 } 733 734 /** 735 * Writes in XML the <code>textStyle</code> object with the given <code>writer</code>. 736 */ writeTextStyle(XMLWriter writer, TextStyle textStyle, final String attributeName)737 protected void writeTextStyle(XMLWriter writer, TextStyle textStyle, 738 final String attributeName) throws IOException { 739 if (textStyle != null) { 740 new ObjectXMLExporter<TextStyle>() { 741 @Override 742 protected void writeAttributes(XMLWriter writer, TextStyle textStyle) throws IOException { 743 writer.writeAttribute("attribute", attributeName, null); 744 writer.writeAttribute("fontName", textStyle.getFontName(), null); 745 writer.writeFloatAttribute("fontSize", textStyle.getFontSize()); 746 writer.writeBooleanAttribute("bold", textStyle.isBold(), false); 747 writer.writeBooleanAttribute("italic", textStyle.isItalic(), false); 748 writer.writeAttribute("alignment", textStyle.getAlignment().name(), TextStyle.Alignment.CENTER.name()); 749 } 750 }.writeElement(writer, textStyle); 751 } 752 } 753 754 /** 755 * Writes in XML the <code>baseboard</code> object with the given <code>writer</code>. 756 */ writeBaseboard(XMLWriter writer, Baseboard baseboard, final String attributeName)757 protected void writeBaseboard(XMLWriter writer, Baseboard baseboard, 758 final String attributeName) throws IOException { 759 if (baseboard != null) { 760 new ObjectXMLExporter<Baseboard>() { 761 @Override 762 protected void writeAttributes(XMLWriter writer, Baseboard baseboard) throws IOException { 763 writer.writeAttribute("attribute", attributeName, null); 764 writer.writeFloatAttribute("thickness", baseboard.getThickness()); 765 writer.writeFloatAttribute("height", baseboard.getHeight()); 766 writer.writeColorAttribute("color", baseboard.getColor()); 767 } 768 769 @Override 770 protected void writeChildren(XMLWriter writer, Baseboard baseboard) throws IOException { 771 writeTexture(writer, baseboard.getTexture(), null); 772 } 773 }.writeElement(writer, baseboard); 774 } 775 } 776 777 /** 778 * Writes in XML the <code>texture</code> object with the given <code>writer</code>. 779 */ writeTexture(XMLWriter writer, HomeTexture texture, final String attributeName)780 protected void writeTexture(XMLWriter writer, HomeTexture texture, 781 final String attributeName) throws IOException { 782 if (texture != null) { 783 new ObjectXMLExporter<HomeTexture>() { 784 @Override 785 protected void writeAttributes(XMLWriter writer, HomeTexture texture) throws IOException { 786 writer.writeAttribute("attribute", attributeName, null); 787 writer.writeAttribute("name", texture.getName(), null); 788 writer.writeAttribute("creator", texture.getCreator(), null); 789 writer.writeAttribute("catalogId", texture.getCatalogId(), null); 790 writer.writeFloatAttribute("width", texture.getWidth()); 791 writer.writeFloatAttribute("height", texture.getHeight()); 792 writer.writeFloatAttribute("xOffset", texture.getXOffset(), 0f); 793 writer.writeFloatAttribute("yOffset", texture.getYOffset(), 0f); 794 writer.writeFloatAttribute("angle", texture.getAngle(), 0f); 795 writer.writeFloatAttribute("scale", texture.getScale(), 1f); 796 writer.writeBooleanAttribute("leftToRightOriented", texture.isLeftToRightOriented(), true); 797 writer.writeAttribute("image", getExportedContentName(texture, texture.getImage()), null); 798 } 799 }.writeElement(writer, texture); 800 } 801 } 802 803 /** 804 * Writes in XML the properties of the <code>HomeObject</code> instance with the given <code>writer</code>. 805 */ writeProperties(XMLWriter writer, HomeObject object)806 private void writeProperties(XMLWriter writer, HomeObject object) throws IOException { 807 List<String> propertiesNames = new ArrayList<String>(object.getPropertyNames()); 808 Collections.sort(propertiesNames); 809 for (String propertyName : propertiesNames) { 810 writeProperty(writer, propertyName, object.getProperty(propertyName)); 811 } 812 } 813 814 /** 815 * Writes in XML the given property. 816 */ writeProperty(XMLWriter writer, String propertyName, String propertyValue)817 private void writeProperty(XMLWriter writer, String propertyName, String propertyValue) throws IOException { 818 if (propertyValue != null) { 819 writer.writeStartElement("property"); 820 writer.writeAttribute("name", propertyName); 821 writer.writeAttribute("value", propertyValue); 822 writer.writeEndElement(); 823 } 824 } 825 826 /** 827 * Returns the string value of the given float, except for -1.0, 1.0 or 0.0 828 * where -1, 1 and 0 is returned. 829 */ floatToString(float f)830 private static String floatToString(float f) { 831 if (Math.abs(f) < 1E-6) { 832 return "0"; 833 } else if (Math.abs(f - 1f) < 1E-6) { 834 return "1"; 835 } else if (Math.abs(f + 1f) < 1E-6) { 836 return "-1"; 837 } else { 838 return String.valueOf(f); 839 } 840 } 841 842 /** 843 * Returns the saved name of the given <code>content</code> owned by an object. 844 */ getExportedContentName(Object owner, Content content)845 protected String getExportedContentName(Object owner, Content content) { 846 if (content == null || this.savedContentNames == null) { 847 return null; 848 } else { 849 String contentName = this.savedContentNames.get(content); 850 if (contentName != null) { 851 return contentName; 852 } else if (content instanceof URLContent) { 853 URLContent urlContent = (URLContent)content; 854 if (urlContent.isJAREntry()) { 855 // Build URL manually to avoid possible values starting by jar://:0 which includes :0 authority 856 // misinterpreted when URL is decoded out of applets environment 857 return urlContent.getURL().getProtocol() + ":" + urlContent.getURL().getFile(); 858 } else { 859 return urlContent.getURL().toString(); 860 } 861 } else { 862 return content.toString(); 863 } 864 } 865 } 866 } 867