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

com.github.fge.jsonschema.jsonpointer.TreePointer Maven / Gradle / Ivy

/*
 * Copyright (c) 2013, Francis Galiegue 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU 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
 * Lesser GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package com.github.fge.jsonschema.jsonpointer;

import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.MissingNode;
import com.github.fge.jsonschema.exceptions.JsonReferenceException;
import com.github.fge.jsonschema.exceptions.unchecked.JsonReferenceError;
import com.github.fge.jsonschema.messages.JsonReferenceMessages;
import com.github.fge.jsonschema.report.ProcessingMessage;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import net.jcip.annotations.ThreadSafe;

import java.util.Iterator;
import java.util.List;

import static com.github.fge.jsonschema.messages.JsonReferenceMessages.NULL_INPUT;

/**
 * A pointer into a {@link TreeNode}
 *
 * 

Note that all pointers are absolute: they start from the root of * the tree. This is to mirror the behaviour of JSON Pointer proper.

* *

The class does not decode a JSON Pointer representation itself; however * it provides all the necessary methods for implementations to achieve this. *

* *

This class has two traversal methods: {@link #get(TreeNode)} and {@link * #path(TreeNode)}. The difference between both is that {@code path()} may * return another node than {@code null} if the tree representation has such * a node. This is the case, for instance, for {@link JsonNode}, which has a * {@link MissingNode}.

* *

At the core, this class is essentially a(n ordered!) {@link List} of * {@link TokenResolver}s (which is iterable via the class itself).

* *

Note that this class' {@link #hashCode()}, {@link #equals(Object)} and * {@link #toString()} are final.

* * @param the type of the tree */ @ThreadSafe public abstract class TreePointer implements Iterable> { /** * The reference token separator */ protected static final char SLASH = '/'; /** * What this tree can see as a missing node (may be {@code null}) */ protected final T missing; /** * The list of token resolvers */ protected final List> tokenResolvers; /** * Main protected constructor * *

This constructor makes an immutable copy of the list it receives as * an argument.

* * @param missing the representation of a missing node (may be null) * @param tokenResolvers the list of reference token resolvers */ protected TreePointer(final T missing, final List> tokenResolvers) { this.missing = missing; this.tokenResolvers = ImmutableList.copyOf(tokenResolvers); } /** * Alternate constructor * *

This is the same as calling {@link #TreePointer(TreeNode, List)} with * {@code null} as the missing node.

* * @param tokenResolvers the list of token resolvers */ protected TreePointer(final List> tokenResolvers) { this(null, tokenResolvers); } /** * Decode an input into a list of reference tokens * * @param input the input * @return the list of reference tokens * @throws JsonReferenceException input is not a valid JSON Pointer * @throws JsonReferenceError input is null */ protected static List tokensFromInput(final String input) throws JsonReferenceException { if (input == null) throw new JsonReferenceError(new ProcessingMessage() .message(NULL_INPUT)); final List ret = Lists.newArrayList(); String s = input; String cooked; int index; char c; while (!s.isEmpty()) { c = s.charAt(0); if (c != SLASH) throw new JsonReferenceException(new ProcessingMessage() .message(JsonReferenceMessages.NOT_SLASH) .put("expected", Character.valueOf(SLASH)) .put("found", Character.valueOf(c))); s = s.substring(1); index = s.indexOf(SLASH); cooked = index == -1 ? s : s.substring(0, index); ret.add(ReferenceToken.fromCooked(cooked)); if (index == -1) break; s = s.substring(index); } return ret; } /** * Traverse a node and return the result * *

Note that this method shortcuts: it stops at the first node it cannot * traverse.

* * @param node the node to traverse * @return the resulting node, {@code null} if not found */ public final T get(final T node) { T ret = node; for (final TokenResolver tokenResolver: tokenResolvers) { if (ret == null) break; ret = tokenResolver.get(ret); } return ret; } /** * Traverse a node and return the result * *

This is like {@link #get(TreeNode)}, but it will return the missing * node if traversal fails.

* * @param node the node to traverse * @return the result, or the missing node * @see #TreePointer(TreeNode, List) */ public final T path(final T node) { final T ret = get(node); return ret == null ? missing : ret; } /** * Tell whether this pointer is empty * * @return true if the reference token list is empty */ public final boolean isEmpty() { return tokenResolvers.isEmpty(); } @Override public final Iterator> iterator() { return tokenResolvers.iterator(); } @Override public final int hashCode() { return tokenResolvers.hashCode(); } @Override public final boolean equals(final Object obj) { if (obj == null) return false; if (this == obj) return true; if (getClass() != obj.getClass()) return false; @SuppressWarnings("unchecked") final TreePointer other = (TreePointer) obj; return tokenResolvers.equals(other.tokenResolvers); } @Override public final String toString() { final StringBuilder sb = new StringBuilder(); /* * This works fine: a TokenResolver's .toString() always returns the * cooked representation of its underlying ReferenceToken. */ for (final TokenResolver tokenResolver: tokenResolvers) sb.append('/').append(tokenResolver); return sb.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy