/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.tinkerpop.gremlin.structure.util; import org.apache.tinkerpop.gremlin.process.computer.Computer; import org.apache.tinkerpop.gremlin.process.computer.ComputerResult; import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; import org.apache.tinkerpop.gremlin.process.computer.MapReduce; import org.apache.tinkerpop.gremlin.process.computer.Memory; import org.apache.tinkerpop.gremlin.process.computer.VertexProgram; import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Translator; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.util.function.FunctionUtils; import org.javatuples.Pair; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; /** * A collection of helpful methods for creating standard {@link Object#toString()} representations of graph-related * objects. * * @author Marko A. Rodriguez (http://markorodriguez.com) */ public final class StringFactory { private static final String V = "v"; private static final String E = "e"; private static final String P = "p"; private static final String VP = "vp"; private static final String L_BRACKET = "["; private static final String R_BRACKET = "]"; private static final String COMMA_SPACE = ", "; private static final String COLON = ":"; private static final String EMPTY_MAP = "{}"; private static final String DOTS = "..."; private static final String DASH = "-"; private static final String ARROW = "->"; private static final String STAR = "*"; private static final String EMPTY_PROPERTY = "p[empty]"; private static final String EMPTY_VERTEX_PROPERTY = "vp[empty]"; private static final String LINE_SEPARATOR = System.getProperty("line.separator"); private static final String STORAGE = "storage"; private static final String featuresStartWith = "supports"; private static final int prefixLength = featuresStartWith.length(); private StringFactory() { } /** * Construct the representation for a {@link org.apache.tinkerpop.gremlin.structure.Vertex}. */ public static String vertexString(final Vertex vertex) { return V + L_BRACKET + vertex.id() + R_BRACKET; } /** * Construct the representation for a {@link org.apache.tinkerpop.gremlin.structure.Edge}. */ public static String edgeString(final Edge edge) { return E + L_BRACKET + edge.id() + R_BRACKET + L_BRACKET + edge.outVertex().id() + DASH + edge.label() + ARROW + edge.inVertex().id() + R_BRACKET; } /** * Construct the representation for a {@link org.apache.tinkerpop.gremlin.structure.Property} or {@link org.apache.tinkerpop.gremlin.structure.VertexProperty}. */ public static String propertyString(final Property property) { if (property instanceof VertexProperty) { if (!property.isPresent()) return EMPTY_VERTEX_PROPERTY; final String valueString = String.valueOf(property.value()); return VP + L_BRACKET + property.key() + ARROW + valueString.substring(0, Math.min(valueString.length(), 20)) + R_BRACKET; } else { if (!property.isPresent()) return EMPTY_PROPERTY; final String valueString = String.valueOf(property.value()); return P + L_BRACKET + property.key() + ARROW + valueString.substring(0, Math.min(valueString.length(), 20)) + R_BRACKET; } } /** * Construct the representation for a {@link org.apache.tinkerpop.gremlin.structure.Graph}. * * @param internalString a mapper {@link String} that appends to the end of the standard representation */ public static String graphString(final Graph graph, final String internalString) { return graph.getClass().getSimpleName().toLowerCase() + L_BRACKET + internalString + R_BRACKET; } public static String graphVariablesString(final Graph.Variables variables) { return "variables" + L_BRACKET + "size:" + variables.keys().size() + R_BRACKET; } public static String memoryString(final Memory memory) { return "memory" + L_BRACKET + "size:" + memory.keys().size() + R_BRACKET; } public static String computeResultString(final ComputerResult computerResult) { return "result" + L_BRACKET + computerResult.graph() + ',' + computerResult.memory() + R_BRACKET; } public static String graphComputerString(final GraphComputer graphComputer) { return graphComputer.getClass().getSimpleName().toLowerCase(); } public static String traversalSourceString(final TraversalSource traversalSource) { final String graphString = traversalSource.getGraph().toString(); final Optional<Computer> optional = VertexProgramStrategy.getComputer(traversalSource.getStrategies()); return traversalSource.getClass().getSimpleName().toLowerCase() + L_BRACKET + graphString + COMMA_SPACE + (optional.isPresent() ? optional.get().toString() : "standard") + R_BRACKET; } public static String featureString(final Graph.Features features) { final StringBuilder sb = new StringBuilder("FEATURES"); final Predicate<Method> supportMethods = (m) -> m.getModifiers() == Modifier.PUBLIC && m.getName().startsWith(featuresStartWith) && !m.getName().equals(featuresStartWith); sb.append(LINE_SEPARATOR); Stream.of(Pair.with(Graph.Features.GraphFeatures.class, features.graph()), Pair.with(Graph.Features.VariableFeatures.class, features.graph().variables()), Pair.with(Graph.Features.VertexFeatures.class, features.vertex()), Pair.with(Graph.Features.VertexPropertyFeatures.class, features.vertex().properties()), Pair.with(Graph.Features.EdgeFeatures.class, features.edge()), Pair.with(Graph.Features.EdgePropertyFeatures.class, features.edge().properties())).forEach(p -> { printFeatureTitle(p.getValue0(), sb); Stream.of(p.getValue0().getMethods()) .filter(supportMethods) .map(createTransform(p.getValue1())) .forEach(sb::append); }); return sb.toString(); } public static String traversalSideEffectsString(final TraversalSideEffects traversalSideEffects) { return "sideEffects" + L_BRACKET + "size:" + traversalSideEffects.keys().size() + R_BRACKET; } public static String traversalStrategiesString(final TraversalStrategies traversalStrategies) { return "strategies" + traversalStrategies.toList(); } public static String traversalStrategyString(final TraversalStrategy traversalStrategy) { return traversalStrategy.getClass().getSimpleName(); } public static String translatorString(final Translator translator) { return "translator[" + translator.getTraversalSource() + ":" + translator.getTargetLanguage() + "]"; } public static String vertexProgramString(final VertexProgram vertexProgram, final String internalString) { return vertexProgram.getClass().getSimpleName() + L_BRACKET + internalString + R_BRACKET; } public static String vertexProgramString(final VertexProgram vertexProgram) { return vertexProgram.getClass().getSimpleName(); } public static String mapReduceString(final MapReduce mapReduce, final String internalString) { return mapReduce.getClass().getSimpleName() + L_BRACKET + internalString + R_BRACKET; } public static String mapReduceString(final MapReduce mapReduce) { return mapReduce.getClass().getSimpleName(); } private static Function<Method, String> createTransform(final Graph.Features.FeatureSet features) { return FunctionUtils.wrapFunction((m) -> ">-- " + m.getName().substring(prefixLength) + ": " + m.invoke(features).toString() + LINE_SEPARATOR); } private static void printFeatureTitle(final Class<? extends Graph.Features.FeatureSet> featureClass, final StringBuilder sb) { sb.append("> "); sb.append(featureClass.getSimpleName()); sb.append(LINE_SEPARATOR); } public static String stepString(final Step<?, ?> step, final Object... arguments) { final StringBuilder builder = new StringBuilder(step.getClass().getSimpleName()); final List<String> strings = Stream.of(arguments) .filter(o -> null != o) .filter(o -> { if (o instanceof TraversalRing) return !((TraversalRing) o).isEmpty(); else if (o instanceof Collection) return !((Collection) o).isEmpty(); else if (o instanceof Map) return !((Map) o).isEmpty(); else return !o.toString().isEmpty(); }) .map(o -> { final String string = o.toString(); return string.contains("$") ? "lambda" : string; }).collect(Collectors.toList()); if (!strings.isEmpty()) { builder.append('('); builder.append(String.join(",", strings)); builder.append(')'); } if (!step.getLabels().isEmpty()) builder.append('@').append(step.getLabels()); //builder.append("^").append(step.getId()); return builder.toString(); } public static String traversalString(final Traversal.Admin<?, ?> traversal) { return traversal.getSteps().toString(); } public static String storageString(final String internalString) { return STORAGE + L_BRACKET + internalString + R_BRACKET; } public static String removeEndBrackets(final Collection collection) { final String string = collection.toString(); return string.substring(1, string.length() - 1); } }