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.metadata.property.value.list;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
  35 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
  36 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
  37 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
  38 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes;
  39 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
  40 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyT;
  41 import com.oracle.javafx.scenebuilder.kit.metadata.property.ValuePropertyMetadata;
  42 import com.oracle.javafx.scenebuilder.kit.metadata.property.value.SingleValuePropertyMetadata;
  43 import com.oracle.javafx.scenebuilder.kit.metadata.util.InspectorPath;
  44 import com.oracle.javafx.scenebuilder.kit.metadata.util.PrefixedValue;
  45 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName;
  46 import java.util.ArrayList;
  47 import java.util.Collections;
  48 import java.util.List;
  49 import java.util.Objects;
  50 import javafx.fxml.FXMLLoader;
  51 
  52 /**
  53  *
  54  */
  55 public abstract class ListValuePropertyMetadata<T> extends ValuePropertyMetadata {
  56 
  57     private final Class<T> itemClass;
  58     private final SingleValuePropertyMetadata<T> itemMetadata;
  59     private final List<T> defaultValue;
  60 
  61     public ListValuePropertyMetadata(PropertyName name,
  62             Class<T> itemClass, SingleValuePropertyMetadata<T> itemMetadata,
  63             boolean readWrite, List<T> defaultValue, InspectorPath inspectorPath) {
  64         super(name, readWrite, inspectorPath);
  65         this.itemClass = itemClass;
  66         this.defaultValue = defaultValue;
  67         this.itemMetadata = itemMetadata;
  68     }
  69 
  70     public Class<T> getItemClass() {
  71         return itemClass;
  72     }
  73 
  74     public List<T> getDefaultValue() {
  75         return defaultValue;
  76     }
  77 
  78     public List<T> getValue(FXOMInstance fxomInstance) {
  79         final List<T> result;
  80 
  81         if (isReadWrite()) {
  82             final FXOMProperty fxomProperty = fxomInstance.getProperties().get(getName());
  83             if (fxomProperty == null) {
  84                 // propertyName is not specified in the fxom instance.
  85                 // We return the default value specified in the metadata of the
  86                 // property
  87                 result = defaultValue;
  88             } else if (fxomProperty instanceof FXOMPropertyT) {
  89                 final FXOMPropertyT fxomPropertyT = (FXOMPropertyT) fxomProperty;
  90                 final PrefixedValue pv = new PrefixedValue(fxomPropertyT.getValue());
  91                 if (pv.isBindingExpression()) {
  92                     result = getDefaultValue();
  93                 } else {
  94                     result = makeValueFromString(fxomPropertyT.getValue());
  95                 }
  96             } else if (fxomProperty instanceof FXOMPropertyC) {
  97                 final FXOMPropertyC fxomPropertyC = (FXOMPropertyC) fxomProperty;
  98                 result = new ArrayList<>();
  99                 for (FXOMObject itemFxomObject : fxomPropertyC.getValues()) {
 100                     if (itemFxomObject instanceof FXOMInstance) {
 101                         final FXOMInstance itemFxomInstance = (FXOMInstance) itemFxomObject;
 102                         result.add(itemMetadata.makeValueFromFxomInstance(itemFxomInstance));
 103                     } else {
 104                         assert false;
 105                     }
 106                 }
 107             } else {
 108                 assert false;
 109                 result = defaultValue;
 110             }
 111         } else {
 112             final List<?> items = (List<?>)getName().getValue(fxomInstance.getSceneGraphObject());
 113             result = new ArrayList<>();
 114             for (Object item : items) {
 115                 result.add(getItemClass().cast(item));
 116             }
 117         }
 118 
 119         return result;
 120     }
 121 
 122     public void setValue(FXOMInstance fxomInstance, List<T> value) {
 123         assert isReadWrite();
 124 
 125         final FXOMProperty fxomProperty = fxomInstance.getProperties().get(getName());
 126 
 127         if (Objects.equals(value, getDefaultValueObject()) || value.isEmpty()) {
 128             // We must remove the fxom property if any
 129             if (fxomProperty != null) {
 130                 fxomProperty.removeFromParentInstance();
 131             }
 132         } else {
 133             final FXOMDocument fxomDocument = fxomInstance.getFxomDocument();
 134             final FXOMProperty newProperty;
 135             if (canMakeStringFromValue(value)) {
 136                 final String valueString = makeStringFromValue(value);
 137                 newProperty = new FXOMPropertyT(fxomDocument, getName(), valueString);
 138             } else {
 139                 final List<FXOMObject> items = new ArrayList<>();
 140                 for (T i : value) {
 141                     items.add(itemMetadata.makeFxomInstanceFromValue(i, fxomDocument));
 142                 }
 143                 newProperty = new FXOMPropertyC(fxomDocument, getName(), items);
 144             }
 145             FXOMNodes.updateProperty(fxomInstance, newProperty);
 146         }
 147     }
 148 
 149 
 150     /*
 151      * To be subclassed
 152      */
 153 
 154     protected boolean canMakeStringFromValue(List<T> value) {
 155         boolean result = true;
 156 
 157         for (T i : value) {
 158             result = itemMetadata.canMakeStringFromValue(i);
 159             if (result == false) {
 160                 break;
 161             }
 162         }
 163 
 164         return result;
 165     }
 166 
 167     protected String makeStringFromValue(List<T> value) {
 168         assert canMakeStringFromValue(value);
 169 
 170         final StringBuilder result = new StringBuilder();
 171 
 172         for (T item : value) {
 173             if (result.length() >= 1) {
 174                 result.append(FXMLLoader.ARRAY_COMPONENT_DELIMITER);
 175                 result.append(' ');
 176             }
 177             result.append(itemMetadata.makeStringFromValue(item));
 178         }
 179 
 180         return result.toString();
 181     }
 182 
 183 
 184     protected List<T> makeValueFromString(String string) {
 185         final List<T> result;
 186 
 187         final String[] items = string.split(FXMLLoader.ARRAY_COMPONENT_DELIMITER);
 188         if (items.length == 0) {
 189             result = Collections.emptyList();
 190         } else {
 191             result = new ArrayList<>();
 192             for (String itemString : items) {
 193                 result.add(itemMetadata.makeValueFromString(itemString));
 194             }
 195         }
 196 
 197         return result;
 198     }
 199 
 200     /*
 201      * ValuePropertyMetadata
 202      */
 203 
 204     @Override
 205     public Class<?> getValueClass() {
 206         return List.class;
 207     }
 208 
 209     @Override
 210     public Object getDefaultValueObject() {
 211         return defaultValue;
 212     }
 213 
 214     @Override
 215     public Object getValueObject(FXOMInstance fxomInstance) {
 216         return getValue(fxomInstance);
 217     }
 218 
 219     @Override
 220     public void setValueObject(FXOMInstance fxomInstance, Object valueObject) {
 221         assert valueObject instanceof List;
 222         setValue(fxomInstance, castItemList((List<?>)valueObject));
 223     }
 224 
 225     /*
 226      * Private
 227      */
 228 
 229     private List<T> castItemList(List<?> valueObject) {
 230         final List<T> result = new ArrayList<>();
 231 
 232         for (Object itemValueObject : valueObject) {
 233             result.add(getItemClass().cast(itemValueObject));
 234         }
 235 
 236         return result;
 237     }
 238 }