Java tutorial
// AVMManager.java --- Manager that handles operations involving AVM nodes. // Copyright (C) 2013-2015 Tim Krones // 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 managers.nodes; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import constants.NodeType; import java.util.ArrayList; import java.util.List; import models.nodes.Feature; import models.nodes.Substructure; import models.nodes.Value; import models.relationships.Has; import play.libs.F.Function; import play.libs.F.Promise; import play.libs.F.Tuple; import play.libs.Json; import utils.UUIDGenerator; public class AVMManager extends LabeledNodeWithPropertiesManager { public AVMManager() { this.label = NodeType.AVM.toString(); } // UPDATE public Promise<Boolean> setValue(final JsonNode avm, final JsonNode feature, final JsonNode value, final JsonNode newValue) { Promise<String> location = beginTransaction(); Promise<Boolean> updated = location.flatMap(new Function<String, Promise<Boolean>>() { public Promise<Boolean> apply(final String location) { Promise<Boolean> updated = setValue(avm, feature, value, newValue, location); return updated.flatMap(new Function<Boolean, Promise<Boolean>>() { public Promise<Boolean> apply(Boolean updated) { if (updated) { return commitTransaction(location); } return Promise.pure(false); } }); } }); return updated; } private Promise<Boolean> setValue(JsonNode avm, JsonNode feature, JsonNode value, final JsonNode newValue, final String location) { final ObjectNode props = Json.newObject(); props.put("rule", avm.get("ruleUUID").asText()); props.put("avm", avm.get("uuid").asText()); final Feature f = new Feature(feature.get("name").asText()); final Value v = new Value(value.get("name").asText()); // 1. Delete :HAS relationship between feature and current value Promise<Boolean> updated = Has.relationships.delete(f, v, props, location); // 2. Create :HAS relationship between feature and new value updated = updated.flatMap(new Function<Boolean, Promise<Boolean>>() { public Promise<Boolean> apply(Boolean updated) { if (updated) { Value n = new Value(newValue.get("name").asText()); return Has.relationships.create(f, n, props, location); } return Promise.pure(false); } }); return updated; } // DELETE @Override protected Promise<Boolean> delete(final JsonNode properties, final String location) { // 1. Remove all features Promise<Boolean> emptied = empty(properties, location); // 2. Delete AVM Promise<Boolean> deleted = emptied.flatMap(new Function<Boolean, Promise<Boolean>>() { public Promise<Boolean> apply(Boolean emptied) { if (emptied) { ObjectNode props = (ObjectNode) properties.deepCopy(); return AVMManager.super.delete(props.retain("uuid"), location); } return Promise.pure(false); } }); return deleted; } private Promise<Boolean> empty(final JsonNode properties, final String location) { // 1. Get list of all features Substructure avm = new Substructure(properties.get("uuid").asText()); Promise<List<JsonNode>> features = Has.relationships.endNodes(avm, location); // 2. Remove each one of them from the AVM Promise<Boolean> emptied = features.flatMap(new Function<List<JsonNode>, Promise<Boolean>>() { public Promise<Boolean> apply(List<JsonNode> features) { return disconnect(properties, features, location); } }); return emptied; } private Promise<Boolean> disconnect(final JsonNode properties, final List<JsonNode> features, final String location) { Promise<Boolean> removed = Promise.pure(true); for (final JsonNode feature : features) { removed = removed.flatMap(new Function<Boolean, Promise<Boolean>>() { public Promise<Boolean> apply(Boolean removed) { if (removed) { return disconnect(properties, feature, location); } return Promise.pure(false); } }); } return removed; } // Connections to other nodes @Override protected Promise<Boolean> connect(JsonNode parentAVM, JsonNode feature, String location) { String parentUUID = parentAVM.get("uuid").asText(); // 1. Connect AVM to feature Substructure avm = new Substructure(parentUUID); Feature feat = new Feature(feature.get("name").asText()); ObjectNode props = Json.newObject(); props.put("rule", parentAVM.get("ruleUUID")); Promise<Boolean> connected = Has.relationships.create(avm, feat, props, location); // 2. If feature is // - atomic, connect to default value ("underspecified") // - complex, // a. create substructure // b. connect feature to substructure String type = feature.get("type").asText(); if (type.equals("atomic")) { connected = connectToValue(feat, props, parentUUID, connected, location); } else if (type.equals("complex")) { connected = connectToAVM(feature, props, parentUUID, connected, location); } return connected; } private Promise<Boolean> connectToValue(final Feature feat, final ObjectNode props, final String parentUUID, Promise<Boolean> connected, final String location) { return connected.flatMap(new Function<Boolean, Promise<Boolean>>() { public Promise<Boolean> apply(Boolean connected) { if (connected) { Value value = new Value("underspecified"); props.put("avm", parentUUID); return Has.relationships.create(feat, value, props, location); } return Promise.pure(false); } }); } private Promise<Boolean> connectToAVM(final JsonNode feature, final JsonNode props, final String parentUUID, Promise<Boolean> connected, final String location) { Promise<Feature> feat = Feature.nodes.get(feature); return connected.zip(feat).flatMap(new Function<Tuple<Boolean, Feature>, Promise<Boolean>>() { public Promise<Boolean> apply(Tuple<Boolean, Feature> t) { Boolean connected = t._1; if (connected) { final Feature feat = t._2; final String subUUID = UUIDGenerator.from(parentUUID + feat.getUUID()); ObjectNode properties = Json.newObject(); properties.put("uuid", subUUID); Promise<Boolean> created = create(properties, location); return created.flatMap(new Function<Boolean, Promise<Boolean>>() { public Promise<Boolean> apply(Boolean created) { if (created) { Substructure avm = new Substructure(subUUID); return Has.relationships.create(feat, avm, props, location); } return Promise.pure(false); } }); } return Promise.pure(false); } }); } @Override protected Promise<Boolean> disconnect(final JsonNode parentAVM, final JsonNode feature, final String location) { final String parentUUID = parentAVM.get("uuid").asText(); final Substructure avm = new Substructure(parentUUID); Feature feat = new Feature(feature.get("name").asText(), feature.get("type").asText()); // 1. Disconnect AVM from feature Promise<Boolean> removed = Has.relationships.delete(avm, feat, location); // 2. Disconnect feature from value removed = disconnectFromValue(feat, parentAVM, removed, location); // 3. If feature is complex, delete value if (feat.isComplex()) { removed = removeValue(feature, parentAVM, removed, location); } return removed; } private Promise<Boolean> disconnectFromValue(final Feature feat, final JsonNode parentAVM, Promise<Boolean> removed, final String location) { return removed.flatMap(new Function<Boolean, Promise<Boolean>>() { public Promise<Boolean> apply(Boolean removed) { if (removed) { final ObjectNode props = Json.newObject(); props.put("rule", parentAVM.get("ruleUUID").asText()); if (feat.isAtomic()) { props.put("avm", parentAVM.get("uuid").asText()); } return Has.relationships.delete(feat, props, location); } return Promise.pure(false); } }); } private Promise<Boolean> removeValue(JsonNode feature, final JsonNode parentAVM, Promise<Boolean> removed, final String location) { Promise<Feature> feat = Feature.nodes.get(feature); return removed.zip(feat).flatMap(new Function<Tuple<Boolean, Feature>, Promise<Boolean>>() { public Promise<Boolean> apply(Tuple<Boolean, Feature> t) { Boolean removed = t._1; if (removed) { Feature feat = t._2; String subUUID = UUIDGenerator.from(parentAVM.get("uuid").asText() + feat.getUUID()); ObjectNode properties = Json.newObject(); properties.put("uuid", subUUID); properties.put("ruleUUID", parentAVM.get("ruleUUID").asText()); return delete(properties, location); } return Promise.pure(false); } }); } // Custom functionality protected Promise<JsonNode> toJSON(final JsonNode properties) { // 1. Get list of all pairs Promise<List<Pair>> pairs = pairs(properties); // 2. Convert pairs to JSON Promise<JsonNode> json = pairs.map(new Function<List<Pair>, JsonNode>() { public JsonNode apply(List<Pair> pairs) { ArrayNode pairNodes = JsonNodeFactory.instance.arrayNode(); for (Pair pair : pairs) { pairNodes.add(pair.toJSON()); } ((ObjectNode) properties).put("pairs", pairNodes); return properties; } }); return json; } private Promise<List<Pair>> pairs(final JsonNode properties) { Promise<List<JsonNode>> features = features(properties); Promise<List<Pair>> pairs = features.flatMap(new Function<List<JsonNode>, Promise<List<Pair>>>() { public Promise<List<Pair>> apply(List<JsonNode> features) { List<Promise<? extends Pair>> pairs = new ArrayList<Promise<? extends Pair>>(); for (JsonNode feature : features) { Promise<JsonNode> attribute = Promise.pure(feature); Promise<JsonNode> value = getValue(properties, feature); Promise<Pair> pair = Pair.of(attribute, value); pairs.add(pair); } return Promise.sequence(pairs); } }); return pairs; } private Promise<List<JsonNode>> features(JsonNode properties) { Substructure avm = new Substructure(properties.get("uuid").asText()); // 1. Get list of all features Promise<List<JsonNode>> nodes = Has.relationships.endNodes(avm); // 2. For each feature, collect targets Promise<List<JsonNode>> features = nodes.flatMap(new Function<List<JsonNode>, Promise<List<JsonNode>>>() { public Promise<List<JsonNode>> apply(List<JsonNode> nodes) { List<Promise<? extends JsonNode>> features = new ArrayList<Promise<? extends JsonNode>>(); for (final JsonNode node : nodes) { ((ObjectNode) node).retain("name", "type"); Promise<List<String>> targets = Feature.nodes.targets(node); Promise<JsonNode> feature = targets.map(new Function<List<String>, JsonNode>() { public JsonNode apply(List<String> targets) { ArrayNode targetNodes = JsonNodeFactory.instance.arrayNode(); for (String target : targets) { targetNodes.add(target); } ((ObjectNode) node).put("targets", targetNodes); return node; } }); features.add(feature); } return Promise.sequence(features); } }); return features; } private Promise<JsonNode> getValue(JsonNode parentAVM, JsonNode feature) { final String parentUUID = parentAVM.get("uuid").asText(); final String ruleUUID = parentAVM.get("ruleUUID").asText(); if (feature.get("type").asText().equals("complex")) { ObjectNode props = ((ObjectNode) feature).deepCopy().retain("name"); Promise<Feature> feat = Feature.nodes.get(props); return feat.flatMap(new Function<Feature, Promise<JsonNode>>() { public Promise<JsonNode> apply(Feature feat) { String subUUID = UUIDGenerator.from(parentUUID + feat.getUUID()); ObjectNode substructure = Json.newObject(); substructure.put("uuid", subUUID); substructure.put("ruleUUID", ruleUUID); return toJSON(substructure); } }); } Feature f = new Feature(feature.get("name").asText()); ObjectNode properties = Json.newObject(); properties.put("rule", ruleUUID); properties.put("avm", parentUUID); Promise<JsonNode> node = Has.relationships.endNode(f, properties); return node.map(new Function<JsonNode, JsonNode>() { public JsonNode apply(JsonNode node) { String name = node.get("name").asText(); return new TextNode(name); } }); } private static class Pair { public JsonNode attribute; public JsonNode value; private Pair(JsonNode attribute, JsonNode value) { this.attribute = attribute; this.value = value; } public static Promise<Pair> of(Promise<JsonNode> attribute, Promise<JsonNode> value) { return attribute.zip(value).map(new Function<Tuple<JsonNode, JsonNode>, Pair>() { public Pair apply(Tuple<JsonNode, JsonNode> pair) { return new Pair(pair._1, pair._2); } }); } public JsonNode toJSON() { ObjectNode json = Json.newObject(); json.put("attribute", this.attribute); json.put("value", this.value); return json; } } }