private static boolean processHasStep(final HasStep<?> step, final Traversal.Admin<?, ?> traversal) {
        if (step.getPreviousStep() instanceof HasStep) {
            final HasStep<?> previousStep = (HasStep<?>) step.getPreviousStep();
            final List<HasContainer> hasContainers = new ArrayList<>(step.getHasContainers());
            for (final HasContainer hasContainer : hasContainers) {
                previousStep.addHasContainer(hasContainer);
            }
            TraversalHelper.copyLabels(step, previousStep, false);
            traversal.removeStep(step);
            return true;
        } else if (step.getPreviousStep() instanceof VertexStep
                && ((VertexStep) step.getPreviousStep()).returnsEdge()
                && 0 == ((VertexStep) step.getPreviousStep()).getEdgeLabels().length) {
            final VertexStep<Edge> previousStep = (VertexStep<Edge>) step.getPreviousStep();
            final List<Object> edgeLabels = new ArrayList<>();
            for (final HasContainer hasContainer : new ArrayList<>(step.getHasContainers())) {
                if (hasContainer.getKey().equals(T.label.getAccessor())) {
                    if (hasContainer.getBiPredicate() == Compare.eq && GValue.instanceOf(hasContainer.getValue(), GType.STRING) &&
                            edgeLabels.isEmpty()) {
                        edgeLabels.add(hasContainer.getValue());
                        step.removeHasContainer(hasContainer);
                    } else if (hasContainer.getBiPredicate() == Contains.within &&
                            hasContainer.getValue() instanceof Collection &&
                            ((Collection) hasContainer.getValue()).containsAll(edgeLabels)) {
                        edgeLabels.addAll((Collection) hasContainer.getValue());
                        step.removeHasContainer(hasContainer);
                    } else if (hasContainer.getPredicate() instanceof OrP && edgeLabels.isEmpty()) {
                        boolean removeContainer = true;
                        final List<P<?>> orps = ((OrP) hasContainer.getPredicate()).getPredicates();
                        final List<Object> newEdges = new ArrayList<>();
                        for (int i = 0; i < orps.size(); i++) {
                            if (orps.get(i).getBiPredicate() == Compare.eq && GValue.instanceOf(orps.get(i).getValue(), GType.STRING))
                                newEdges.add(orps.get(i).getValue());
                            else {
                                removeContainer = false;
                                break;
                            }
                        }
                        if (removeContainer) {
                            edgeLabels.addAll(newEdges);
                            step.removeHasContainer(hasContainer);
                        }
                    }
                }
            }
            if (!edgeLabels.isEmpty()) {
                final VertexStep<Edge> newVertexStep = new VertexStep<>(traversal, Edge.class, previousStep.getDirection(), GValue.ensureGValues(edgeLabels.toArray()));
                TraversalHelper.replaceStep(previousStep, newVertexStep, traversal);
                TraversalHelper.copyLabels(previousStep, newVertexStep, false);
                if (step.getHasContainers().isEmpty()) {
                    TraversalHelper.copyLabels(step, newVertexStep, false);
                    traversal.removeStep(step);
                }
                return true;
            }
            return false;
        } else
            return false;
    }
This strategy analyzes filter-steps with child traversals that themselves are pure filters. If the child traversals
are pure filters then the wrapping parent filter is not needed and thus, the children can be "inlined." Normalizing
pure filters with inlining reduces the number of variations of a filter that a graph provider may need to reason
about when writing their own strategies. As a result, this strategy helps increase the likelihood that a provider's
filtering optimization will succeed at re-writing the traversal.

@author Marko A. Rodriguez (http://markorodriguez.com)
@example <pre>
__.outE().hasLabel(eq("knows").or(eq("created"))).inV()       // is replaced by __.outE("knows", "created").inV()
__.filter(has("name","marko"))                                // is replaced by __.has("name","marko")
__.and(has("name"),has("age"))                                // is replaced by __.has("name").has("age")
__.and(filter(has("name","marko").has("age")),hasNot("blah")) // is replaced by __.has("name","marko").has("age").hasNot("blah")
__.match(as('a').has(key,value),...)                          // is replaced by __.as('a').has(key,value).match(...)
</pre>
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java