All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.api.tools.framework.util.ProtoPathTree Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2017 Google, Inc.
 *
 * 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 com.google.api.tools.framework.util;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TreeMap;

/**
 * This class is used for storing data that are indexed by a SourceCodeInfo.Location "path" field
 * (which is defined as "repeated int32"). Conceptually, the paths form a n-ary tree where (for
 * example) path [a,b] is a parent node of [a,b,c,d] & [a,b,c,d,e,f] & [a,b,g,h].
 *
 * 

For details about these path lists, see: * https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/DescriptorProtos.SourceCodeInfo.Location * *

Each instance of ProtoPathTree is a node in the tree. The entries in its map correspond to its * child node. Data elements stored at this node are managed via addDataElement() & * getDataElements() methods. */ public class ProtoPathTree extends TreeMap> { private final ProtoPathTree parent; private final ProtoPathWrapper pathFromRoot; private final List dataElements = new ArrayList<>(); private boolean isDeleted = false; public ProtoPathTree() { this.parent = null; this.pathFromRoot = ProtoPathWrapper.EMPTY_PATH; } public ProtoPathTree(ProtoPathTree parent, int childPathIndex) { this.parent = Preconditions.checkNotNull(parent, "Parent node must be specified for the child ctor."); ProtoPathWrapper parentPathFromRoot = parent.getPathFromRoot(); this.pathFromRoot = ProtoHelpers.buildPath(parentPathFromRoot, childPathIndex); parent.put(childPathIndex, this); } public boolean isRootNode() { return parent == null; } /** * Returns the parent node of this node in the tree. Throws an exception if there is no parent. */ public ProtoPathTree getParent() { return parent; } /** * Returns the child nodes of this one, in numerical order based on the "path" indexes of each * child. */ public Collection> getChildren() { return values(); } /** * Returns the path to get to this node of the tree from the root. The path from the root node to * itself is an empty list. */ public ProtoPathWrapper getPathFromRoot() { return pathFromRoot; } /** * Visitor that visits each node in the tree, recursively. Child nodes will be visited in the same * order that getChildren() returns them in. */ public static class Visitor extends GenericVisitor { public Visitor() { super(ProtoPathTree.class); } @Accepts public void acceptNode(ProtoPathTree tree) { for (Object child : tree.getChildren()) { visit((ProtoPathTree) child); } } } /** Returns the subtree rooted at 'childPath' into which 'dataElement' is added. */ public ProtoPathTree addDataElement(ProtoPathWrapper childPath, DATA dataElement) { if (childPath.isEmpty()) { dataElements.add(dataElement); return this; } ProtoPathTree subtree = getSubtree(childPath, true); return subtree.addDataElement(ProtoPathWrapper.EMPTY_PATH, dataElement); } /** Returns the data stored at this node of the tree. Returns an empty list if none are here. */ public ImmutableList getDataElements() { return ImmutableList.copyOf(this.dataElements); } /** * Marks this node of the tree for deletion. Since some of the integers in a path are index * numbers, doing a "mark for delete" means that the index numbers aren't invalidated. */ public boolean markForDeletion(boolean isDeleted) { boolean ret = this.isDeleted; this.isDeleted = isDeleted; return ret; } public boolean isMarkedForDeletion() { return isDeleted; } /** Gets the subtree rooted at the specified child path (path relative to this node). */ public ProtoPathTree getSubtree(ProtoPathWrapper childPath) { return getSubtree(childPath, false); } /** * Gets the subtree rooted at the specified child path (path relative to this node), creating * nodes along the way as needed. */ public ProtoPathTree getSubtree(ProtoPathWrapper childPath, boolean autoCreate) { ProtoPathTree currSubtree = this; // Descend into the tree along the path defined by 'childPath', auto-creating intermediate // nodes, if needed. for (Integer childPathIndex = 0; childPathIndex < childPath.getDepth(); childPathIndex++) { Integer childPathElement = childPath.getPathElement(childPathIndex); if (currSubtree.containsKey(childPathElement)) { currSubtree = currSubtree.get(childPathElement); } else if (autoCreate) { currSubtree = new ProtoPathTree<>(currSubtree, childPathElement); } else { return null; } } return currSubtree; } /** Removes the subtree rooted at the specified child path (path relative to this node). */ public void removeSubtree(ProtoPathWrapper childPath) { if (childPath.isEmpty()) { // No child specified --> no-op return; } ProtoPathTree childSubtree = getSubtree(childPath); if (childSubtree == null) { throw new IllegalArgumentException( String.format("Specified path %s doesn't exist, so can't be removed.", childPath)); } ProtoPathTree parentSubtree = childSubtree.getParent(); Integer childPathIndex = childPath.getPathElement(childPath.getDepth() - 1); parentSubtree.remove(childPathIndex); } /** Format tree and its children into a string, primarily for debugging output. */ public String format() { return format("", this); } public String format(String indent) { return format(indent, this); } public String format(String indent, ProtoPathTree tree) { return MoreObjects.toStringHelper(tree) .add("isDeleted", tree.isDeleted) .add("dataElements", tree.dataElements) .add("children", formatChildren(indent + "| ", tree)) .toString(); } public String formatChildren(final String indent, final ProtoPathTree tree) { return Joiner.on("") .join( FluentIterable.from(tree.keySet()) .transform( new Function() { @Override public String apply(Integer key) { return String.format( "\n%s[%d]=%s", indent, key, format(indent, tree.get(key))); } })); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy