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 
  33 package com.oracle.javafx.scenebuilder.kit.editor.job.reference;
  34 
  35 import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
  36 import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
  37 import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveNodeJob;
  38 import com.oracle.javafx.scenebuilder.kit.editor.job.atomic.RemoveObjectJob;
  39 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMCollection;
  40 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
  41 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMInstance;
  42 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNode;
  43 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMNodes;
  44 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
  45 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMProperty;
  46 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMPropertyC;
  47 import java.util.LinkedList;
  48 import java.util.List;
  49 
  50 /**
  51  */
  52 
  53 public class ObjectDeleter {
  54 
  55     private final EditorController editorController;
  56     private final FXOMDocument fxomDocument;
  57     private final List<Job> executedJobs = new LinkedList<>();
  58 
  59     public ObjectDeleter(EditorController editorController) {
  60         assert editorController != null;
  61         assert editorController.getFxomDocument() != null;
  62         this.editorController = editorController;
  63         this.fxomDocument = editorController.getFxomDocument();
  64     }
  65 
  66     public void delete(FXOMObject target) {
  67         final FXOMNode node = prepareDeleteObject(target, target);
  68 
  69         if (node == target) {
  70             final RemoveObjectJob removeJob = new RemoveObjectJob(target, editorController);
  71             removeJob.execute();
  72             executedJobs.add(removeJob);
  73         }
  74     }
  75 
  76     public void prepareDelete(FXOMObject target) {
  77         assert target != null;
  78         assert target.getFxomDocument() == fxomDocument;
  79         assert fxomDocument.getFxomRoot() != null; // At least target
  80 
  81         prepareDeleteObject(target, target);
  82     }
  83 
  84     public List<Job> getExecutedJobs() {
  85         return new LinkedList<>(executedJobs);
  86     }
  87 
  88 
  89     /*
  90      * Private
  91      */
  92 
  93     private FXOMNode prepareDeleteObject(FXOMObject node, FXOMObject target) {
  94         final FXOMNode result;
  95 
  96         final String nodeFxId = node.getFxId();
  97         if (nodeFxId == null) {
  98             // node has no fx:id : it can be deleted safely
  99             result = node;
 100         } else {
 101             final FXOMObject fxomRoot = fxomDocument.getFxomRoot();
 102             final List<FXOMNode> references = fxomRoot.collectReferences(nodeFxId, target);
 103             if (references.isEmpty()) {
 104                 // node has an fx:id but this one is not referenced
 105                 // outside of the delete target : it can be deleted safely
 106                 result = node;
 107             } else {
 108                 // node has an fx:id referenced outside of the delete target
 109                 // => we find the first strong reference R to it
 110                 // => we remove all the weak references between node and R
 111                 // => we combine node with R
 112                 FXOMNode firstReference = null;
 113                 for (FXOMNode r : references) {
 114                     if (FXOMNodes.isWeakReference(r)) {
 115                         // This weak reference will become a forward reference
 116                         // after the deletion => we remove it.
 117                         final Job clearJob = new RemoveNodeJob(r, editorController);
 118                         clearJob.execute();
 119                         executedJobs.add(clearJob);
 120                     } else {
 121                         firstReference = r;
 122                         break;
 123                     }
 124                 }
 125 
 126                 if (firstReference == null) {
 127                     // node has only weak references ; those references have
 128                     // been removed => node can be delete safely
 129                     result = node;
 130                 } else {
 131                     // we combine firstReference with node ie node is
 132                     // disconnected from its parent and put in place of
 133                     // firstReference
 134                     final Job combineJob = new CombineReferenceJob(firstReference, editorController);
 135                     combineJob.execute();
 136                     executedJobs.add(combineJob);
 137                     result = null;
 138                 }
 139             }
 140         }
 141 
 142         if (result == node) {
 143             if (node instanceof FXOMInstance) {
 144                 final FXOMInstance fxomInstance = (FXOMInstance) node;
 145                 for (FXOMProperty p : new LinkedList<>(fxomInstance.getProperties().values())) {
 146                     if (p instanceof FXOMPropertyC) {
 147                         final FXOMPropertyC cp = (FXOMPropertyC) p;
 148                         for (FXOMObject value : new LinkedList<>(cp.getValues())) {
 149                             prepareDeleteObject(value, target);
 150                         }
 151                     }
 152                 }
 153             } else if (result instanceof FXOMCollection) {
 154                 final FXOMCollection fxomCollection = (FXOMCollection) result;
 155                 for (FXOMObject i : new LinkedList<>(fxomCollection.getItems())) {
 156                     prepareDeleteObject(i, target);
 157                 }
 158             } // else no prework needed
 159         }
 160 
 161         return result;
 162     }
 163 }