org.terasology.logic.behavior.asset.BehaviorTreeFormat.java Source code

Java tutorial

Introduction

Here is the source code for org.terasology.logic.behavior.asset.BehaviorTreeFormat.java

Source

/*
 * Copyright 2014 MovingBlocks
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.terasology.logic.behavior.asset;

import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.terasology.utilities.Assets;
import org.terasology.assets.ResourceUrn;
import org.terasology.assets.format.AbstractAssetFileFormat;
import org.terasology.assets.format.AssetDataFile;
import org.terasology.assets.module.annotations.RegisterAssetFileFormat;
import org.terasology.engine.ComponentFieldUri;
import org.terasology.logic.behavior.tree.LookupNode;
import org.terasology.logic.behavior.tree.Node;
import org.terasology.reflection.metadata.ClassMetadata;
import org.terasology.registry.CoreRegistry;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.NotSerializableException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.Map;

/**
 * Loader for behavior assets. Can also save assets into json format.
 * <br><br>
 * If there are both, Nodes and Renderables tree, both are loaded/saved. To ensure, the nodes get associated to
 * the correct renderable, additional ids are introduced (only in the json file).
 * <br><br>
 *
 */
@RegisterAssetFileFormat
public class BehaviorTreeFormat extends AbstractAssetFileFormat<BehaviorTreeData> {
    private BehaviorTreeGson treeGson = new BehaviorTreeGson();

    public BehaviorTreeFormat() {
        super("behavior");
    }

    public void save(OutputStream stream, BehaviorTreeData data) throws IOException {
        try (JsonWriter write = new JsonWriter(new OutputStreamWriter(stream, Charsets.UTF_8))) {
            write.setIndent("  ");
            write.beginObject().name("model");
            treeGson.saveTree(write, data.getRoot());
            write.endObject();
        }
    }

    @Override
    public BehaviorTreeData load(ResourceUrn resourceUrn, List<AssetDataFile> list) throws IOException {
        NodesClassLibrary nodesClassLibrary = CoreRegistry.get(NodesClassLibrary.class);
        if (nodesClassLibrary == null) {
            throw new IOException("Could not load behavior tree, NodeClassLibrary not available");
        }
        try (InputStream stream = list.get(0).openStream()) {
            return load(stream);
        }
    }

    public BehaviorTreeData load(InputStream stream) throws IOException {
        BehaviorTreeData data = new BehaviorTreeData();
        try (JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8))) {
            reader.setLenient(true);
            reader.beginObject();
            nextName(reader, "model");
            data.setRoot(treeGson.loadTree(reader));
            reader.endObject();
        } catch (JsonSyntaxException e) {
            throw new IOException("Cannot load tree! " + e.getMessage(), e);
        }
        return data;
    }

    private String nextName(JsonReader in, String expectedName) throws IOException {
        String name = in.nextName();
        if (!expectedName.equals(name)) {
            throw new RuntimeException(expectedName + " expected!");
        }
        return name;
    }

    private final class BehaviorTreeGson {
        private int currentId;
        private Map<Node, Integer> nodeIds = Maps.newHashMap();
        private Map<Integer, Node> idNodes = Maps.newHashMap();

        private Gson gsonNode;

        private BehaviorTreeGson() {
            gsonNode = new GsonBuilder().setPrettyPrinting()
                    .registerTypeAdapterFactory(new NodeTypeAdapterFactory())
                    .registerTypeAdapter(BehaviorTree.class, new BehaviorTreeTypeAdapterFactory())
                    .registerTypeAdapter(ComponentFieldUri.class, new ComponentFieldUriAdapterFactory()).create();
        }

        public Node loadTree(JsonReader reader) {
            resetIds();
            return gsonNode.fromJson(reader, Node.class);
        }

        public void saveTree(JsonWriter writer, Node root) {
            resetIds();
            gsonNode.toJson(root, Node.class, writer);
        }

        public Node getNode(int id) {
            return idNodes.get(id);
        }

        public int getId(Node node) {
            return nodeIds.get(node);
        }

        private void resetIds() {
            idNodes.clear();
            nodeIds.clear();
            currentId = 0;
        }

        private class NodeTypeAdapterFactory implements TypeAdapterFactory {
            @Override
            public <T> TypeAdapter<T> create(final Gson gson, TypeToken<T> type) {
                final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
                return new TypeAdapter<T>() {
                    private NodesClassLibrary nodesClassLibrary = CoreRegistry.get(NodesClassLibrary.class);

                    @Override
                    public void write(JsonWriter out, T value) throws IOException {
                        if (value instanceof Node) {
                            Node node = (Node) value;
                            ClassMetadata<?, ?> metadata = nodesClassLibrary.getMetadata(node.getClass());

                            if (metadata != null) {
                                out.beginObject();
                                idNodes.put(currentId, node);
                                nodeIds.put(node, currentId);

                                TypeAdapter<T> delegateAdapter = getDelegateAdapter(value.getClass());

                                String metadataVal = metadata.getUri().toString();
                                out.name("nodeType").value(metadataVal).name("nodeId").value(currentId);
                                currentId++;
                                out.name("node");
                                delegateAdapter.write(out, value);
                                out.endObject();
                            } else {
                                delegate.write(out, null);
                            }
                        } else {
                            delegate.write(out, value);
                        }
                    }

                    @Override
                    public T read(JsonReader in) throws IOException {
                        if (in.peek() == JsonToken.BEGIN_OBJECT) {
                            in.beginObject();
                            nextName(in, "nodeType");
                            String nodeType = in.nextString();
                            ClassMetadata<? extends Node, ?> classMetadata = CoreRegistry
                                    .get(NodesClassLibrary.class).resolve(nodeType);
                            nextName(in, "nodeId");
                            int id = in.nextInt();
                            nextName(in, "node");
                            if (classMetadata != null) {
                                TypeAdapter<T> delegateAdapter = getDelegateAdapter(classMetadata.getType());
                                Node result;
                                if (classMetadata.getType() == LookupNode.class) {
                                    result = classMetadata.newInstance();
                                    in.beginObject();
                                    nextName(in, "tree");
                                    String uri = in.nextString();
                                    ((LookupNode) result).tree = Assets.get(uri, BehaviorTree.class).orElse(null);
                                    in.endObject();
                                } else {
                                    result = (Node) delegateAdapter.read(in);
                                }
                                idNodes.put(id, result);
                                nodeIds.put(result, id);
                                in.endObject();
                                return (T) result;
                            } else {
                                throw new NotSerializableException(nodeType);
                            }
                        } else {
                            return delegate.read(in);
                        }
                    }

                    private TypeAdapter<T> getDelegateAdapter(Class<?> cls) {
                        return (TypeAdapter<T>) gson.getDelegateAdapter(NodeTypeAdapterFactory.this,
                                TypeToken.get(cls));
                    }
                };
            }

        }

        private class BehaviorTreeTypeAdapterFactory extends TypeAdapter<BehaviorTree> {
            @Override
            public void write(JsonWriter out, BehaviorTree value) throws IOException {
                if (value == null) {
                    out.value("");
                } else {
                    out.value(value.getUrn().toString());
                }
            }

            @Override
            public BehaviorTree read(JsonReader in) throws IOException {
                String uri = in.nextString();
                if (uri != null && uri.length() > 0) {
                    return Assets.get(in.nextString(), BehaviorTree.class).orElse(null);
                } else {
                    return null;
                }
            }
        }

        private class ComponentFieldUriAdapterFactory extends TypeAdapter<ComponentFieldUri> {
            @Override
            public void write(JsonWriter out, ComponentFieldUri value) throws IOException {
                if (value == null) {
                    out.value("");
                } else {
                    out.value(value.toString());
                }
            }

            @Override
            public ComponentFieldUri read(JsonReader in) throws IOException {
                String uri = in.nextString();
                if (uri != null && uri.length() > 0) {
                    return new ComponentFieldUri(uri);
                } else {
                    return null;
                }
            }
        }
    }
}