/* * ToroDB * Copyright © 2014 8Kdata Technology (www.8kdata.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.torodb.mongodb.language.update; import com.torodb.core.document.ToroDocument; import com.torodb.kvdocument.values.KvArray; import com.torodb.kvdocument.values.KvDocument; import com.torodb.kvdocument.values.KvDocument.DocEntry; import com.torodb.kvdocument.values.KvValue; import com.torodb.kvdocument.values.heap.MapKvDocument; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; /** * */ public class UpdatedToroDocumentBuilder { private LinkedHashMap<String, KvValue<?>> values = new LinkedHashMap<>(); private final Map<String, UpdatedToroDocumentArrayBuilder> subArrayBuilders = new HashMap<>(); private final Map<String, UpdatedToroDocumentBuilder> subObjectBuilders = new HashMap<>(); private boolean built = false; private boolean updated = false; private UpdatedToroDocumentBuilder() { } public void clear() { if (built) { built = false; updated = false; values = new LinkedHashMap<>(); } else { values.clear(); } subArrayBuilders.clear(); subObjectBuilders.clear(); } public static UpdatedToroDocumentBuilder create() { return new UpdatedToroDocumentBuilder(); } public static UpdatedToroDocumentBuilder from(ToroDocument original) { UpdatedToroDocumentBuilder result = UpdatedToroDocumentBuilder.create(); result.copy(original); return result; } public static UpdatedToroDocumentBuilder from(KvDocument original) { UpdatedToroDocumentBuilder result = UpdatedToroDocumentBuilder.create(); result.copy(original); return result; } public boolean contains(String key) { return isValue(key) || isArrayBuilder(key) || isObjectBuilder(key); } public boolean isValue(String key) { return values.containsKey(key); } @Nonnull public KvValue<?> getValue(String key) { KvValue<?> result = values.get(key); if (result == null) { throw new IllegalArgumentException( "There is no value associated to '" + key + "' key"); } return result; } public boolean isArrayBuilder(String key) { return subArrayBuilders.containsKey(key); } @Nonnull public UpdatedToroDocumentArrayBuilder getArrayBuilder(String key) { UpdatedToroDocumentArrayBuilder result = subArrayBuilders.get(key); if (result == null) { throw new IllegalArgumentException( "There is no array builder associated to '" + key + "' key"); } return result; } public boolean isObjectBuilder(String key) { return subObjectBuilders.containsKey(key); } @Nonnull public UpdatedToroDocumentBuilder getObjectBuilder(String key) { UpdatedToroDocumentBuilder result = subObjectBuilders.get(key); if (result == null) { throw new IllegalArgumentException( "There is no object builder associated to '" + key + "' key"); } return result; } public UpdatedToroDocumentBuilder putValue(String key, KvValue<?> value) { checkNewBuild(); if (value instanceof KvDocument) { newObject(key).copy((KvDocument) value); } else if (value instanceof KvArray) { newArray(key).copy((KvArray) value); } else { values.put(key, value); subArrayBuilders.remove(key); subObjectBuilders.remove(key); } return this; } public UpdatedToroDocumentArrayBuilder newArray(String key) { checkNewBuild(); UpdatedToroDocumentArrayBuilder result = UpdatedToroDocumentArrayBuilder.create(); values.remove(key); subArrayBuilders.put(key, result); subObjectBuilders.remove(key); return result; } public UpdatedToroDocumentBuilder newObject(String key) { checkNewBuild(); UpdatedToroDocumentBuilder result = UpdatedToroDocumentBuilder.create(); values.remove(key); subArrayBuilders.remove(key); subObjectBuilders.put(key, result); return result; } public boolean unset(String key) { boolean result = false; result |= values.remove(key) != null; result |= subArrayBuilders.remove(key) != null; result |= subObjectBuilders.remove(key) != null; return result; } public KvDocument buildRoot() { built = true; for (Entry<String, UpdatedToroDocumentBuilder> objectBuilder : subObjectBuilders.entrySet()) { KvValue<?> oldValue = values.put( objectBuilder.getKey(), objectBuilder.getValue().buildRoot() ); assert oldValue == null; } for (Entry<String, UpdatedToroDocumentArrayBuilder> arrayBuilder : subArrayBuilders.entrySet()) { KvValue<?> oldValue = values.put( arrayBuilder.getKey(), arrayBuilder.getValue().build() ); assert oldValue == null; } subObjectBuilders.clear(); subArrayBuilders.clear(); return new MapKvDocument(values); } void copy(ToroDocument original) { copy(original.getRoot()); } void copy(KvDocument original) { values.clear(); subArrayBuilders.clear(); subObjectBuilders.clear(); for (DocEntry<?> entry : original) { KvValue<?> value = entry.getValue(); if (value instanceof KvArray) { UpdatedToroDocumentArrayBuilder childBuilder = newArray(entry.getKey()); childBuilder.copy((KvArray) value); } else if (value instanceof KvDocument) { UpdatedToroDocumentBuilder childBuilder = newObject(entry.getKey()); childBuilder.copy((KvDocument) value); } else { putValue(entry.getKey(), value); } } } private void checkNewBuild() { if (built) { values = new LinkedHashMap<>(); built = false; } } public UpdatedToroDocumentBuilder setUpdated() { this.updated = true; return this; } public boolean isUpdated() { return updated; } public KvDocument build() { KvDocument updatedDocument = buildRoot(); clear(); return updatedDocument; } }