001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.schematic.internal.document; 017 018import java.util.Collections; 019import java.util.Iterator; 020import java.util.List; 021import java.util.stream.Collectors; 022import org.modeshape.schematic.document.Array; 023import org.modeshape.schematic.document.Changes; 024import org.modeshape.schematic.document.Editor; 025import org.modeshape.schematic.internal.delta.AddValueIfAbsentOperation; 026import org.modeshape.schematic.internal.delta.AddValueOperation; 027import org.modeshape.schematic.internal.delta.ClearOperation; 028import org.modeshape.schematic.internal.delta.Operation; 029import org.modeshape.schematic.internal.delta.PutIfAbsentOperation; 030import org.modeshape.schematic.internal.delta.PutOperation; 031import org.modeshape.schematic.internal.delta.RemoveAllValuesOperation; 032import org.modeshape.schematic.internal.delta.RemoveAtIndexOperation; 033import org.modeshape.schematic.internal.delta.RemoveOperation; 034import org.modeshape.schematic.internal.delta.RemoveValueOperation; 035import org.modeshape.schematic.internal.delta.RetainAllValuesOperation; 036import org.modeshape.schematic.internal.delta.SetValueOperation; 037 038/** 039 * Document editor implementation which allows the editing of a document via a list of incremental changes. 040 * 041 * @author Horia Chiorean (hchiorea@redhat.com) 042 * @since 5.0 043 */ 044public class IncrementalDocumentEditor extends ObservableDocumentEditor implements Editor { 045 046 private static final long serialVersionUID = 1L; 047 048 private transient final List<Operation> operations; 049 050 public IncrementalDocumentEditor(MutableDocument document, 051 List<Operation> operations) { 052 super(document, Paths.rootPath(), operations::add, null); 053 this.operations = operations; 054 } 055 056 @Override 057 public Changes getChanges() { 058 return new DocumentChanges(operations != null ? operations : Collections.emptyList()); 059 } 060 061 @Override 062 public void apply(Changes changes) { 063 apply(changes.clone(), null); 064 } 065 066 private static Array.Entry newEntry(int index, 067 Object value) { 068 return new BasicArray.BasicEntry(index, value); 069 } 070 071 @Override 072 public void apply(Changes changes, 073 Observer observer) { 074 if (changes.isEmpty()) { 075 return; 076 } 077 MutableDocument mutable = asMutableDocument(); 078 for (Operation operation : (DocumentChanges) changes) { 079 operation.replay(mutable); 080 if (observer != null) { 081 if (operation instanceof SetValueOperation) { 082 SetValueOperation op = (SetValueOperation) operation; 083 observer.setArrayValue(op.getParentPath(), newEntry(op.getIndex(), op.getValue())); 084 } else if (operation instanceof AddValueOperation) { 085 AddValueOperation op = (AddValueOperation) operation; 086 if (op.getActualIndex() != -1) { 087 observer.addArrayValue(op.getParentPath(), newEntry(op.getActualIndex(), op.getValue())); 088 } 089 } else if (operation instanceof AddValueIfAbsentOperation) { 090 AddValueIfAbsentOperation op = (AddValueIfAbsentOperation) operation; 091 if (op.isAdded()) { 092 observer.addArrayValue(op.getParentPath(), newEntry(op.getIndex(), op.getValue())); 093 } 094 } else if (operation instanceof RemoveValueOperation) { 095 RemoveValueOperation op = (RemoveValueOperation) operation; 096 if (op.getActualIndex() != -1) { 097 observer.removeArrayValue(op.getParentPath(), newEntry(op.getActualIndex(), op.getRemovedValue())); 098 } 099 } else if (operation instanceof RemoveAtIndexOperation) { 100 RemoveAtIndexOperation op = (RemoveAtIndexOperation) operation; 101 observer.removeArrayValue(op.getParentPath(), newEntry(op.getIndex(), op.getRemovedValue())); 102 } else if (operation instanceof RetainAllValuesOperation) { 103 RetainAllValuesOperation op = (RetainAllValuesOperation) operation; 104 for (Array.Entry entry : op.getRemovedEntries()) { 105 observer.removeArrayValue(op.getParentPath(), entry); 106 } 107 } else if (operation instanceof RemoveAllValuesOperation) { 108 RemoveAllValuesOperation op = (RemoveAllValuesOperation) operation; 109 for (Array.Entry entry : op.getRemovedEntries()) { 110 observer.removeArrayValue(op.getParentPath(), entry); 111 } 112 } else if (operation instanceof ClearOperation) { 113 ClearOperation op = (ClearOperation) operation; 114 observer.clear(op.getParentPath()); 115 } else if (operation instanceof PutOperation) { 116 PutOperation op = (PutOperation) operation; 117 observer.put(op.getParentPath(), op.getFieldName(), op.getNewValue()); 118 } else if (operation instanceof PutIfAbsentOperation) { 119 PutIfAbsentOperation op = (PutIfAbsentOperation) operation; 120 if (op.isApplied()) { 121 observer.put(op.getParentPath(), op.getFieldName(), op.getNewValue()); 122 } 123 } else if (operation instanceof RemoveOperation) { 124 RemoveOperation op = (RemoveOperation) operation; 125 if (op.isRemoved()) { 126 observer.remove(op.getParentPath(), op.getFieldName()); 127 } 128 } 129 } 130 } 131 } 132 133 protected static class DocumentChanges implements Changes, Iterable<Operation> { 134 135 private final List<Operation> operations; 136 137 protected DocumentChanges( List<Operation> operations ) { 138 this.operations = operations; 139 } 140 141 @Override 142 public DocumentChanges clone() { 143 List<Operation> newOps = operations.stream().map(Operation::clone).collect(Collectors.toList()); 144 return new DocumentChanges(newOps); 145 } 146 147 @Override 148 public Iterator<Operation> iterator() { 149 return operations.iterator(); 150 } 151 152 @Override 153 public boolean isEmpty() { 154 return operations.isEmpty(); 155 } 156 157 @Override 158 public String toString() { 159 StringBuilder sb = new StringBuilder(); 160 Iterator<Operation> iter = operations.iterator(); 161 if (iter.hasNext()) { 162 sb.append(iter.next()); 163 while (iter.hasNext()) { 164 sb.append("\n").append(iter.next()); 165 } 166 } 167 return sb.toString(); 168 } 169 } 170}