1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.oracle.javafx.scenebuilder.kit.fxom; 33 34 import com.oracle.javafx.scenebuilder.kit.fxom.glue.GlueElement; 35 import com.oracle.javafx.scenebuilder.kit.metadata.property.value.list.StringListPropertyMetadata; 36 import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue; 37 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName; 38 import java.net.MalformedURLException; 39 import java.net.URL; 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.Map; 43 44 /** 45 * 46 * 47 */ 48 public class FXOMPropertyT extends FXOMProperty { 49 50 /* 51 * A FXOMPropertyT represents a property with a single value that 52 * can be represented as text : String, Boolean, Integer, Double, Enum... 53 * 54 * There are three ways to express such a property in FXML. 55 * Let's take Button.text as an example: 56 * 57 * 1) <Button text="OK" /> 58 * value = "OK" 59 * propertyElement == null 60 * valueElement == null 61 * 62 * 2) <Button><text>OK</text></Button> 63 * value = "OK" 64 * propertyElement != null 65 * && propertyElement.getTagName() == "text" 66 * && propertyElement.getChildren().size() == 0 67 * && propertyElement.getContentText().equals("OK") 68 * 69 * 3) <Button><text><String fx:value="OK"/></text></Button> 70 * value = "OK" 71 * propertyElement != null 72 * && propertyElement.getTagName() == "text" 73 * && propertyElement.getChildren().size() == 1 74 * && propertyElement.getChildren().get(0) == valueElement 75 * valueElement != null 76 * && valueElement.getAttributes().get("fx:value").equals("OK") 77 */ 78 79 private String value; 80 private final GlueElement propertyElement; 81 private final GlueElement valueElement; 82 83 public FXOMPropertyT(FXOMDocument document, PropertyName name, GlueElement propertyElement, GlueElement valueElement, String value) { 84 super(document, name); 85 this.propertyElement = propertyElement; 86 this.valueElement = valueElement; 87 this.value = value; 88 } 89 90 public FXOMPropertyT(FXOMDocument document, PropertyName name, String value) { 91 super(document, name); 92 assert value != null; 93 this.propertyElement = null; 94 this.valueElement = null; 95 this.value = value; 96 } 97 98 public String getValue() { 99 return value; 100 } 101 102 public void setValue(String newValue) { 103 assert newValue != null; 104 105 106 if (propertyElement != null) { 107 if (valueElement != null) { // Case #3 108 final Map<String,String> attributes = valueElement.getAttributes(); 109 assert attributes.get("fx:value") != null; 110 assert attributes.get("fx:value").equals(value); 111 attributes.put("fx:value", newValue); 112 113 } else { // Case #2 114 assert propertyElement.getContentText() != null; 115 assert propertyElement.getContentText().equals(value); 116 propertyElement.setContentText(newValue); 117 } 118 } else { // Case #1 119 final FXOMInstance parentInstance = getParentInstance(); 120 if (parentInstance != null) { 121 final GlueElement parentElement = parentInstance.getGlueElement(); 122 final Map<String, String> parentAttributes = parentElement.getAttributes(); 123 assert parentAttributes.get(getName().toString()).equals(value); 124 parentAttributes.put(getName().toString(), newValue); 125 } 126 } 127 128 value = newValue; 129 } 130 131 public GlueElement getPropertyElement() { 132 return propertyElement; 133 } 134 135 public GlueElement getValueElement() { 136 return valueElement; 137 } 138 139 /* 140 * FXOMProperty 141 */ 142 143 @Override 144 public void addToParentInstance(int index, FXOMInstance newParentInstance) { 145 146 assert newParentInstance != null; 147 148 if (getParentInstance() != null) { 149 removeFromParentInstance(); 150 } 151 152 setParentInstance(newParentInstance); 153 newParentInstance.addProperty(this); 154 155 final GlueElement newParentElement = newParentInstance.getGlueElement(); 156 157 if (propertyElement == null) { // Case #1 158 // index is ignored 159 final Map<String,String> attributes = newParentElement.getAttributes(); 160 assert attributes.get(getName().toString()) == null; 161 attributes.put(getName().toString(), value); 162 } else { // Case #2 or #3 163 assert -1 <= index; 164 assert index <= newParentElement.getChildren().size(); 165 propertyElement.addToParent(index, newParentElement); 166 } 167 } 168 169 170 @Override 171 public void removeFromParentInstance() { 172 173 assert getParentInstance() != null; 174 175 final FXOMInstance currentParentInstance = getParentInstance(); 176 final GlueElement currentParentElement = currentParentInstance.getGlueElement(); 177 178 if (propertyElement == null) { // Case #1 179 final Map<String,String> attributes = currentParentElement.getAttributes(); 180 assert attributes.get(getName().toString()) != null; 181 attributes.remove(getName().toString()); 182 } else { // Case #2 or #3 183 propertyElement.removeFromParent(); 184 } 185 186 setParentInstance(null); 187 currentParentInstance.removeProperty(this); 188 } 189 190 191 @Override 192 public int getIndexInParentInstance() { 193 final int result; 194 195 if (getParentInstance() == null) { 196 result = -1; 197 } else if (propertyElement == null) { // Case #1 198 result = -1; 199 } else { // Case #2 or #3 200 final GlueElement parentElement = getParentInstance().getGlueElement(); 201 result = parentElement.getChildren().indexOf(propertyElement); 202 assert result != -1; 203 } 204 205 return result; 206 } 207 208 209 /* 210 * FXOMNode 211 */ 212 213 @Override 214 public void moveToFxomDocument(FXOMDocument destination) { 215 assert destination != null; 216 assert destination != getFxomDocument(); 217 218 documentLocationWillChange(destination.getLocation()); 219 220 if (getParentInstance() != null) { 221 assert getParentInstance().getFxomDocument() == getFxomDocument(); 222 removeFromParentInstance(); 223 } 224 225 assert getParentInstance() == null; 226 assert (propertyElement == null) || (propertyElement.getParent() == null); 227 228 if (propertyElement != null) { 229 propertyElement.moveToDocument(destination.getGlue()); 230 assert (valueElement == null) || (valueElement.getDocument() == destination.getGlue()); 231 } 232 changeFxomDocument(destination); 233 } 234 235 236 @Override 237 protected void changeFxomDocument(FXOMDocument destination) { 238 assert destination != null; 239 assert destination != getFxomDocument(); 240 assert (propertyElement == null) || (destination.getGlue() == propertyElement.getDocument()); 241 242 super.changeFxomDocument(destination); 243 } 244 245 @Override 246 public void documentLocationWillChange(URL newLocation) { 247 final URL currentLocation = getFxomDocument().getLocation(); 248 249 final List<String> currentItems = StringListPropertyMetadata.splitValue(getValue()); 250 final List<String> newItems = new ArrayList<>(); 251 int changeCount = 0; 252 for (String currentItem : currentItems) { 253 final PrefixedValue pv = new PrefixedValue(currentItem); 254 if (pv.isDocumentRelativePath()) { 255 assert currentLocation != null; 256 257 /* 258 * currentItem is a path relative to currentLocation. 259 * We compute the absolute path and, if new location 260 * is non null, we relativize the absolute path against 261 * newLocation. 262 */ 263 final URL assetURL = pv.resolveDocumentRelativePath(currentLocation); 264 final String newValue; 265 if (newLocation == null) { 266 newValue = assetURL.toString(); 267 } else { 268 final PrefixedValue pv2 269 = PrefixedValue.makePrefixedValue(assetURL, newLocation); 270 newValue = pv2.toString(); 271 } 272 newItems.add(newValue); 273 changeCount++; 274 } else if (pv.isPlainString() && (currentLocation == null)) { 275 276 /* 277 * currentItem is a plain string. 278 * We check if it is an URL. 279 * 280 * Since currentLocation is null and newLocation non null, 281 * then all URLs should be converted to relative path. 282 */ 283 assert newLocation != null; 284 try { 285 final URL assetURL = new URL(pv.getSuffix()); 286 final PrefixedValue pv2 = PrefixedValue.makePrefixedValue(assetURL, newLocation); 287 newItems.add(pv2.toString()); 288 changeCount++; 289 } catch(MalformedURLException x) { 290 // p.getValue() is not an URL 291 // We keep currentItem unchanged. 292 newItems.add(currentItem); 293 } 294 } else { 295 newItems.add(currentItem); 296 } 297 } 298 assert currentItems.size() == newItems.size(); 299 300 if (changeCount >= 1) { 301 setValue(StringListPropertyMetadata.assembleValue(newItems)); 302 } 303 304 } 305 306 }