
net.named_data.jndn.Name Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jndn Show documentation
Show all versions of jndn Show documentation
jNDN is a new implementation of a Named Data Networking client library written in Java. It is wire format compatible with the new NDN-TLV encoding, with NDNx and PARC's CCNx.
/**
* Copyright (C) 2013-2016 Regents of the University of California.
* @author: Jeff Thompson
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
* A copy of the GNU Lesser General Public License is in the file COPYING.
*/
package net.named_data.jndn;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import net.named_data.jndn.encoding.EncodingException;
import net.named_data.jndn.encoding.WireFormat;
import net.named_data.jndn.util.Blob;
import net.named_data.jndn.util.ChangeCountable;
import net.named_data.jndn.encoding.tlv.TlvEncoder;
/**
* A Name holds an array of Name.Component and represents an NDN name.
*/
public class Name implements ChangeCountable, Comparable {
/**
* A Name.Component holds a read-only name component value.
*/
public static class Component implements Comparable {
/**
* Create a new GENERIC Name.Component with a zero-length value.
*/
public
Component()
{
value_ = new Blob(ByteBuffer.allocate(0), false);
type_ = ComponentType.GENERIC;
}
/**
* Create a new GENERIC Name.Component, using the existing the Blob value.
* (To create an ImplicitSha256Digest component, use fromImplicitSha256Digest.)
* @param value The component value. value may not be null, but
* value.buf() may be null.
*/
public
Component(Blob value)
{
if (value == null)
throw new NullPointerException("Component: Blob value may not be null");
value_ = value;
type_ = ComponentType.GENERIC;
}
/**
* Create a new Name.Component, taking another pointer to the component's
* read-only value.
* @param component The component to copy.
*/
public
Component(Component component)
{
value_ = component.value_;
type_ = component.type_;
}
/**
* Create a new GENERIC Name.Component, copying the given value.
* (To create an ImplicitSha256Digest component, use fromImplicitSha256Digest.)
* @param value The value byte array.
*/
public
Component(byte[] value)
{
value_ = new Blob(value, true);
type_ = ComponentType.GENERIC;
}
/**
* Create a new GENERIC Name.Component, converting the value to UTF8 bytes.
* Note, this does not escape %XX values. If you need to escape, use
* Name.fromEscapedString.
* @param value The string to convert to UTF8.
*/
public
Component(String value)
{
value_ = new Blob(value);
type_ = ComponentType.GENERIC;
}
/**
* Get the component value.
* @return The component value.
*/
public final Blob
getValue() { return value_; }
/**
* Write this component value to result, escaping characters according to
* the NDN URI Scheme. This also adds "..." to a value with zero or more ".".
* This adds a type code prefix as needed, such as "sha256digest=".
* @param result The StringBuffer to write to.
*/
public final void
toEscapedString(StringBuffer result)
{
if (type_ == ComponentType.IMPLICIT_SHA256_DIGEST) {
result.append("sha256digest=");
Blob.toHex(value_.buf(), result);
}
else
Name.toEscapedString(value_.buf(), result);
}
/**
* Convert this component value by escaping characters according to the
* NDN URI Scheme. This also adds "..." to a value with zero or more ".".
* This adds a type code prefix as needed, such as "sha256digest=".
* @return The escaped string.
*/
public final String
toEscapedString()
{
StringBuffer result = new StringBuffer(value_.buf().remaining());
toEscapedString(result);
return result.toString();
}
/**
* Check if this component is a segment number according to NDN naming
* conventions for "Segment number" (marker 0x00).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @return True if this is a segment number.
*/
public final boolean
isSegment()
{
return value_.size() >= 1 && value_.buf().get(0) == (byte)0x00 && isGeneric();
}
/**
* Check if this component is a segment byte offset according to NDN
* naming conventions for segment "Byte offset" (marker 0xFB).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @return True if this is a segment byte offset.
*/
public final boolean
isSegmentOffset()
{
return value_.size() >= 1 && value_.buf().get(0) == (byte)0xFB && isGeneric();
}
/**
* Check if this component is a version number according to NDN naming
* conventions for "Versioning" (marker 0xFD).
* @return True if this is a version number.
*/
public final boolean
isVersion()
{
return value_.size() >= 1 && value_.buf().get(0) == (byte)0xFD && isGeneric();
}
/**
* Check if this component is a timestamp according to NDN naming
* conventions for "Timestamp" (marker 0xFC).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @return True if this is a timestamp.
*/
public final boolean
isTimestamp()
{
return value_.size() >= 1 && value_.buf().get(0) == (byte)0xFC && isGeneric();
}
/**
* Check if this component is a sequence number according to NDN naming
* conventions for "Sequencing" (marker 0xFE).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @return True if this is a sequence number.
*/
public final boolean
isSequenceNumber()
{
return value_.size() >= 1 && value_.buf().get(0) == (byte)0xFE && isGeneric();
}
/**
* Check if this component is a generic component.
* @return True if this is an generic component.
*/
public final boolean
isGeneric()
{
return type_ == ComponentType.GENERIC;
}
/**
* Check if this component is an ImplicitSha256Digest component.
* @return True if this is an ImplicitSha256Digest component.
*/
public final boolean
isImplicitSha256Digest()
{
return type_ == ComponentType.IMPLICIT_SHA256_DIGEST;
}
/**
* Interpret this name component as a network-ordered number and return an
* integer.
* @return The integer number.
*/
public final long
toNumber()
{
ByteBuffer buffer = value_.buf();
if (buffer == null)
return 0;
long result = 0;
for (int i = buffer.position(); i < buffer.limit(); ++i) {
result *= 256;
result += (long)((int)buffer.get(i) & 0xff);
}
return result;
}
/**
* Interpret this name component as a network-ordered number with a marker
* and return an integer.
* @param marker The required first byte of the component.
* @return The integer number.
* @throws EncodingException If the first byte of the component does not
* equal the marker.
*/
public final long
toNumberWithMarker(int marker) throws EncodingException
{
ByteBuffer buffer = value_.buf();
if (buffer == null || buffer.remaining() <= 0 || buffer.get(0) != (byte)marker)
throw new EncodingException
("Name component does not begin with the expected marker.");
long result = 0;
for (int i = buffer.position() + 1; i < buffer.limit(); ++i) {
result *= 256;
result += (long)((int)buffer.get(i) & 0xff);
}
return result;
}
/**
* Interpret this name component as a segment number according to NDN naming
* conventions for "Segment number" (marker 0x00).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @return The integer segment number.
* @throws EncodingException If the first byte of the component is not the
* expected marker.
*/
public final long
toSegment() throws EncodingException
{
return toNumberWithMarker(0x00);
}
/**
* Interpret this name component as a segment byte offset according to NDN
* naming conventions for segment "Byte offset" (marker 0xFB).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @return The integer segment byte offset.
* @throws EncodingException If the first byte of the component is not the
* expected marker.
*/
public final long
toSegmentOffset() throws EncodingException
{
return toNumberWithMarker(0xFB);
}
/**
* Interpret this name component as a version number according to NDN naming
* conventions for "Versioning" (marker 0xFD). Note that this returns
* the exact number from the component without converting it to a time
* representation.
* @return The integer version number.
* @throws EncodingException If the first byte of the component is not the
* expected marker.
*/
public final long
toVersion() throws EncodingException
{
return toNumberWithMarker(0xFD);
}
/**
* Interpret this name component as a timestamp according to NDN naming
* conventions for "Timestamp" (marker 0xFC).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @return The number of microseconds since the UNIX epoch (Thursday,
* 1 January 1970) not counting leap seconds.
* @throws EncodingException If the first byte of the component is not the
* expected marker.
*/
public final long
toTimestamp() throws EncodingException
{
return toNumberWithMarker(0xFC);
}
/**
* Interpret this name component as a sequence number according to NDN naming
* conventions for "Sequencing" (marker 0xFE).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @return The integer sequence number.
* @throws EncodingException If the first byte of the component is not the
* expected marker.
*/
public final long
toSequenceNumber() throws EncodingException
{
return toNumberWithMarker(0xFE);
}
/**
* Create a component whose value is the nonNegativeInteger encoding of the
* number.
* @param number The number to be encoded.
* @return The component value.
*/
public static Component
fromNumber(long number)
{
if (number < 0)
number = 0;
TlvEncoder encoder = new TlvEncoder(8);
encoder.writeNonNegativeInteger(number);
return new Component(new Blob(encoder.getOutput(), false));
}
/**
* Create a component whose value is the marker appended with the
* nonNegativeInteger encoding of the number.
* @param number The number to be encoded.
* @param marker The marker to use as the first byte of the component.
* @return The component value.
*/
public static Component
fromNumberWithMarker(long number, int marker)
{
if (number < 0)
number = 0;
TlvEncoder encoder = new TlvEncoder(9);
// Encode backwards.
encoder.writeNonNegativeInteger(number);
encoder.writeNonNegativeInteger((long)marker);
return new Component(new Blob(encoder.getOutput(), false));
}
/**
* Create a component with the encoded segment number according to NDN
* naming conventions for "Segment number" (marker 0x00).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @param segment The segment number.
* @return The new Component.
*/
public static Component
fromSegment(long segment)
{
return fromNumberWithMarker(segment, 0x00);
}
/**
* Create a component with the encoded segment byte offset according to NDN
* naming conventions for segment "Byte offset" (marker 0xFB).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @param segmentOffset The segment byte offset.
* @return The new Component.
*/
public static Component
fromSegmentOffset(long segmentOffset)
{
return fromNumberWithMarker(segmentOffset, 0xFB);
}
/**
* Create a component with the encoded version number according to NDN
* naming conventions for "Versioning" (marker 0xFD).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* Note that this encodes the exact value of version without converting from a
* time representation.
* @param version The version number.
* @return The new Component.
*/
public static Component
fromVersion(long version)
{
return fromNumberWithMarker(version, 0xFD);
}
/**
* Create a component with the encoded timestamp according to NDN naming
* conventions for "Timestamp" (marker 0xFC).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @param timestamp The number of microseconds since the UNIX epoch (Thursday,
* 1 January 1970) not counting leap seconds.
* @return The new Component.
*/
public static Component
fromTimestamp(long timestamp)
{
return fromNumberWithMarker(timestamp, 0xFC);
}
/**
* Create a component with the encoded sequence number according to NDN naming
* conventions for "Sequencing" (marker 0xFE).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @param sequenceNumber The sequence number.
* @return The new Component.
*/
public static Component
fromSequenceNumber(long sequenceNumber)
{
return fromNumberWithMarker(sequenceNumber, 0xFE);
}
/**
* Create a component of type ImplicitSha256DigestComponent, so that
* isImplicitSha256Digest() is true.
* @param digest The SHA-256 digest value.
* @return The new Component.
* @throws EncodingException If the digest length is not 32 bytes.
*/
public static Component
fromImplicitSha256Digest(Blob digest) throws EncodingException
{
if (digest.size() != 32)
throw new EncodingException
("Name.Component.fromImplicitSha256Digest: The digest length must be 32 bytes");
Component result = new Component(digest);
result.type_ = ComponentType.IMPLICIT_SHA256_DIGEST;
return result;
}
/**
* Create a component of type ImplicitSha256DigestComponent, so that
* isImplicitSha256Digest() is true.
* @param digest The SHA-256 digest value.
* @return The new Component.
* @throws EncodingException If the digest length is not 32 bytes.
*/
public static Component
fromImplicitSha256Digest(byte[] digest) throws EncodingException
{
return fromImplicitSha256Digest(new Blob(digest));
}
/**
* Get the successor of this component, as described in Name.getSuccessor.
* @return A new Name.Component which is the successor of this.
*/
public final Component
getSuccessor()
{
// Allocate an extra byte in case the result is larger.
ByteBuffer result = ByteBuffer.allocate(value_.size() + 1);
boolean carry = true;
for (int i = value_.size() - 1; i >= 0; --i) {
if (carry) {
// b & 0xff makes the byte unsigned and returns an int.
int x = value_.buf().get(value_.buf().position() + i) & 0xff;
x = (x + 1) & 0xff;
result.put(i, (byte)x);
carry = (x == 0);
}
else
result.put(i, value_.buf().get(value_.buf().position() + i));
}
if (carry)
// Assume all the bytes were set to zero (or the component was empty).
// In NDN ordering, carry does not mean to prepend a 1, but to make a
// component one byte longer of all zeros.
result.put(result.limit() - 1, (byte)0);
else
// We didn't need the extra byte.
result.limit(value_.size());
return new Component(new Blob(result, false));
}
/**
* Check if this is the same component as other.
* @param other The other Component to compare with.
* @return True if the components are equal, otherwise false.
*/
public final boolean
equals(Component other)
{
return value_.equals(other.value_) && type_ == other.type_;
}
public boolean
equals(Object other)
{
if (!(other instanceof Component))
return false;
return equals((Component)other);
}
public int hashCode()
{
return 37 * type_.getNumericType() + value_.hashCode();
}
/**
* Compare this to the other Component using NDN canonical ordering.
* @param other The other Component to compare with.
* @return 0 If they compare equal, -1 if this comes before other in the
* canonical ordering, or 1 if this comes after other in the canonical
* ordering.
*/
public final int
compare(Component other)
{
if (type_.getNumericType() < other.type_.getNumericType())
return -1;
if (type_.getNumericType() > other.type_.getNumericType())
return 1;
if (value_.size() < other.value_.size())
return -1;
if (value_.size() > other.value_.size())
return 1;
// The components are equal length. Just do a byte compare.
return value_.compare(other.value_);
}
public final int
compareTo(Object o) { return this.compare((Component)o); }
// Also include this version for portability.
public final int
CompareTo(Object o) { return this.compare((Component)o); }
/**
* Reverse the bytes in buffer starting at position, up to but not including
* limit.
* @param buffer
* @param position
* @param limit
*/
private static void reverse(ByteBuffer buffer, int position, int limit)
{
int from = position;
int to = limit - 1;
while (from < to) {
// swap
byte temp = buffer.get(from);
buffer.put(from, buffer.get(to));
buffer.put(to, temp);
--to;
++from;
}
}
/**
* A ComponentType specifies the recognized types of a name component.
*/
private enum ComponentType {
IMPLICIT_SHA256_DIGEST(1),
GENERIC(8);
ComponentType(int type)
{
type_ = type;
}
public final int
getNumericType() { return type_; }
private final int type_;
}
// Note: We keep the type_ internal because it is only used to distinguish
// from ImplicitSha256Digest. If we support general typed components then
// we can provide public access.
private ComponentType type_;
private final Blob value_;
}
/**
* Create a new Name with no components.
*/
public
Name()
{
components_ = new ArrayList();
}
/**
* Create a new Name with the components in the given name.
* @param name The name with components to copy from.
*/
public
Name(Name name)
{
components_ = new ArrayList(name.components_);
}
/**
* Create a new Name, copying the components.
* @param components The components to copy.
*/
public
Name(ArrayList components)
{
// Don't need to deep-copy Component elements because they are read-only.
components_ = new ArrayList(components);
}
/**
* Create a new Name, copying the components.
* @param components The components to copy.
*/
public
Name(Component[] components)
{
components_ = new ArrayList();
for (int i = 0; i < components.length; ++i)
components_.add(components[i]);
}
/**
* Parse the uri according to the NDN URI Scheme and create the name with the
* components.
* @param uri The URI string.
*/
public
Name(String uri)
{
components_ = new ArrayList();
set(uri);
}
/**
* Get the number of components.
* @return The number of components.
*/
public final int
size() { return components_.size(); }
/**
* Get the component at the given index.
* @param i The index of the component, starting from 0. However, if i is
* negative, return the component at size() - (-i).
* @return The name component at the index.
*/
public final Component
get(int i)
{
if (i >= 0)
return components_.get(i);
else
return components_.get(components_.size() - (-i));
}
public final void
set(String uri)
{
clear();
uri = uri.trim();
if (uri.length() == 0)
return;
int iColon = uri.indexOf(':');
if (iColon >= 0) {
// Make sure the colon came before a '/'.
int iFirstSlash = uri.indexOf('/');
if (iFirstSlash < 0 || iColon < iFirstSlash)
// Omit the leading protocol such as ndn:
uri = uri.substring(iColon + 1).trim();
}
// Trim the leading slash and possibly the authority.
if (uri.charAt(0) == '/') {
if (uri.length() >= 2 && uri.charAt(1) == '/') {
// Strip the authority following "//".
int iAfterAuthority = uri.indexOf('/', 2);
if (iAfterAuthority < 0)
// Unusual case: there was only an authority.
return;
else
uri = uri.substring(iAfterAuthority + 1).trim();
}
else
uri = uri.substring(1).trim();
}
int iComponentStart = 0;
// Unescape the components.
String sha256digestPrefix = "sha256digest=";
while (iComponentStart < uri.length()) {
int iComponentEnd = uri.indexOf("/", iComponentStart);
if (iComponentEnd < 0)
iComponentEnd = uri.length();
Component component;
if (sha256digestPrefix.regionMatches
(0, uri, iComponentStart, sha256digestPrefix.length())) {
try {
component = Component.fromImplicitSha256Digest
(fromHex(uri, iComponentStart + sha256digestPrefix.length(),
iComponentEnd));
} catch (EncodingException ex) {
throw new Error(ex.getMessage());
}
}
else
component = new Component
(fromEscapedString(uri, iComponentStart, iComponentEnd));
// Ignore illegal components. This also gets rid of a trailing '/'.
if (!component.getValue().isNull())
append(component);
iComponentStart = iComponentEnd + 1;
}
}
/**
* Clear all the components.
*/
public final void
clear()
{
components_.clear();
++changeCount_;
}
/**
* Append a new GENERIC component, copying from value.
* (To append an ImplicitSha256Digest component, use appendImplicitSha256Digest.)
* @param value The component value.
* @return This name so that you can chain calls to append.
*/
public final Name
append(byte[] value)
{
return append(new Component(value));
}
/**
* Append a new GENERIC component, using the existing Blob value.
* (To append an ImplicitSha256Digest component, use appendImplicitSha256Digest.)
* @param value The component value.
* @return This name so that you can chain calls to append.
*/
public final Name
append(Blob value)
{
return append(new Component(value));
}
/**
* Append the component to this name.
* @param component The component to append.
* @return This name so that you can chain calls to append.
*/
public final Name
append(Component component)
{
components_.add(component);
++changeCount_;
return this;
}
public final Name
append(Name name)
{
if (name == this)
// Copying from this name, so need to make a copy first.
return append(new Name(name));
for (int i = 0; i < name.components_.size(); ++i)
append(name.get(i));
return this;
}
/**
* Convert the value to UTF8 bytes and append a Name.Component.
* Note, this does not escape %XX values. If you need to escape, use
* Name.fromEscapedString. Also, if the string has "/", this does not split
* into separate components. If you need that then use
* append(new Name(value)).
* @param value The string to convert to UTF8.
* @return This name so that you can chain calls to append.
*/
public final Name
append(String value)
{
return append(new Component(value));
}
/**
* Get a new name, constructed as a subset of components.
* @param iStartComponent The index if the first component to get. If
* iStartComponent is -N then return return components starting from
* name.size() - N.
* @param nComponents The number of components starting at iStartComponent.
* If greater than the size of this name, get until the end of the name.
* @return A new name.
*/
public final Name
getSubName(int iStartComponent, int nComponents)
{
if (iStartComponent < 0)
iStartComponent = components_.size() - (-iStartComponent);
Name result = new Name();
int iEnd = iStartComponent + nComponents;
for (int i = iStartComponent; i < iEnd && i < components_.size(); ++i)
result.components_.add(components_.get(i));
return result;
}
/**
* Get a new name, constructed as a subset of components starting at
* iStartComponent until the end of the name.
* @param iStartComponent The index if the first component to get. If
* iStartComponent is -N then return return components starting from
* name.size() - N.
* @return A new name.
*/
public final Name
getSubName(int iStartComponent)
{
return getSubName(iStartComponent, components_.size());
}
/**
* Return a new Name with the first nComponents components of this Name.
* @param nComponents The number of prefix components. If nComponents is -N
* then return the prefix up to name.size() - N. For example getPrefix(-1)
* returns the name without the final component.
* @return A new Name.
*/
public final Name
getPrefix(int nComponents)
{
if (nComponents < 0)
return getSubName(0, components_.size() + nComponents);
else
return getSubName(0, nComponents);
}
/**
* Encode this name as a URI according to the NDN URI Scheme.
* @param includeScheme If true, include the "ndn:" scheme in the URI, e.g.
* "ndn:/example/name". If false, just return the path, e.g. "/example/name",
* which is normally the case where toUri() is used for display.
* @return The URI string.
*/
public final String
toUri(boolean includeScheme)
{
if (components_.isEmpty())
return includeScheme ? "ndn:/" : "/";
StringBuffer result = new StringBuffer();
if (includeScheme)
result.append("ndn:");
for (int i = 0; i < components_.size(); ++i) {
result.append("/");
get(i).toEscapedString(result);
}
return result.toString();
}
/**
* Encode this name as a URI according to the NDN URI Scheme. Just return the
* path, e.g. "/example/name" which is the default case where toUri() is used
* for display.
* @return The URI string.
*/
public final String
toUri()
{
return toUri(false);
}
public String toString() { return toUri(); }
/**
* Append a component with the encoded segment number according to NDN
* naming conventions for "Segment number" (marker 0x00).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @param segment The segment number.
* @return This name so that you can chain calls to append.
*/
public final Name
appendSegment(long segment)
{
return append(Component.fromSegment(segment));
}
/**
* Append a component with the encoded segment byte offset according to NDN
* naming conventions for segment "Byte offset" (marker 0xFB).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @param segmentOffset The segment byte offset.
* @return This name so that you can chain calls to append.
*/
public final Name
appendSegmentOffset(long segmentOffset)
{
return append(Component.fromSegmentOffset(segmentOffset));
}
/**
* Append a component with the encoded version number according to NDN
* naming conventions for "Versioning" (marker 0xFD).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* Note that this encodes the exact value of version without converting from a
* time representation.
* @param version The version number.
* @return This name so that you can chain calls to append.
*/
public final Name
appendVersion(long version)
{
return append(Component.fromVersion(version));
}
/**
* Append a component with the encoded timestamp according to NDN naming
* conventions for "Timestamp" (marker 0xFC).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @param timestamp The number of microseconds since the UNIX epoch (Thursday,
* 1 January 1970) not counting leap seconds.
* @return This name so that you can chain calls to append.
*/
public final Name
appendTimestamp(long timestamp)
{
return append(Component.fromTimestamp(timestamp));
}
/**
* Append a component with the encoded sequence number according to NDN naming
* conventions for "Sequencing" (marker 0xFE).
* http://named-data.net/doc/tech-memos/naming-conventions.pdf
* @param sequenceNumber The sequence number.
* @return This name so that you can chain calls to append.
*/
public final Name
appendSequenceNumber(long sequenceNumber)
{
return append(Component.fromSequenceNumber(sequenceNumber));
}
/**
* Append a component of type ImplicitSha256DigestComponent, so that
* isImplicitSha256Digest() is true.
* @param digest The SHA-256 digest value.
* @return This name so that you can chain calls to append.
* @throws EncodingException If the digest length is not 32 bytes.
*/
public final Name
appendImplicitSha256Digest(Blob digest) throws EncodingException
{
return append(Component.fromImplicitSha256Digest(digest));
}
/**
* Append a component of type ImplicitSha256DigestComponent, so that
* isImplicitSha256Digest() is true.
* @param digest The SHA-256 digest value.
* @return This name so that you can chain calls to append.
* @throws EncodingException If the digest length is not 32 bytes.
*/
public final Name
appendImplicitSha256Digest(byte[] digest) throws EncodingException
{
return append(Component.fromImplicitSha256Digest(digest));
}
/**
* Check if this name has the same component count and components as the given
* name.
* @param name The Name to check.
* @return true if the names are equal, otherwise false.
*/
public boolean
equals(Name name)
{
if (components_.size() != name.components_.size())
return false;
// Check from last to first since the last components are more likely to differ.
for (int i = components_.size() - 1; i >= 0; --i) {
if (!get(i).getValue().equals(name.get(i).getValue()))
return false;
}
return true;
}
public boolean
equals(Object other)
{
if (!(other instanceof Name))
return false;
return equals((Name)other);
}
public int hashCode()
{
if (hashCodeChangeCount_ != getChangeCount()) {
// The values have changed, so the previous hash code is invalidated.
haveHashCode_ = false;
hashCodeChangeCount_ = getChangeCount();
}
if (!haveHashCode_) {
int hashCode = 0;
// Use a similar hash code algorithm as String.
for (int i = 0; i < components_.size(); ++i)
hashCode = 37 * hashCode + components_.get(i).hashCode();
hashCode_ = hashCode;
haveHashCode_ = true;
}
return hashCode_;
}
/**
* Get the successor of this name which is defined as follows.
*
* N represents the set of NDN Names, and X,Y ∈ N.
* Operator < is defined by the NDN canonical order on N.
* Y is the successor of X, if (a) X < Y, and (b) ∄ Z ∈ N s.t. X < Z < Y.
*
* In plain words, the successor of a name is the same name, but with its last
* component advanced to a next possible value.
*
* Examples:
*
* - The successor of / is /%00
* - The successor of /%00%01/%01%02 is /%00%01/%01%03
* - The successor of /%00%01/%01%FF is /%00%01/%02%00
* - The successor of /%00%01/%FF%FF is /%00%01/%00%00%00
*
* @return A new name which is the successor of this.
*/
public final Name
getSuccessor()
{
if (size() == 0) {
// Return "/%00".
Name result = new Name();
result.append(new byte[1]);
return result;
}
else
return getPrefix(-1).append(get(-1).getSuccessor());
}
/**
* Check if the N components of this name are the same as the first N
* components of the given name.
* @param name The Name to check.
* @return true if this matches the given name, otherwise false. This always
* returns true if this name is empty.
*/
public final boolean
match(Name name)
{
// This name is longer than the name we are checking it against.
if (components_.size() > name.components_.size())
return false;
// Check if at least one of given components doesn't match. Check from last
// to first since the last components are more likely to differ.
for (int i = components_.size() - 1; i >= 0; --i) {
if (!get(i).getValue().equals(name.get(i).getValue()))
return false;
}
return true;
}
/**
* Check if the N components of this name are the same as the first N
* components of the given name.
* @param name The Name to check.
* @return true if this matches the given name, otherwise false. This always
* returns true if this name is empty.
*/
public final boolean
isPrefixOf(Name name) { return match(name); }
/**
* Encode this Name for a particular wire format.
* @param wireFormat A WireFormat object used to encode this Name.
* @return The encoded buffer.
*/
public final Blob
wireEncode(WireFormat wireFormat)
{
return wireFormat.encodeName(this);
}
/**
* Encode this Name for the default wire format
* WireFormat.getDefaultWireFormat().
* @return The encoded buffer.
*/
public final Blob
wireEncode()
{
return wireEncode(WireFormat.getDefaultWireFormat());
}
/**
* Decode the input using a particular wire format and update this Name.
* @param input The input buffer to decode. This reads from position() to
* limit(), but does not change the position.
* @param wireFormat A WireFormat object used to decode the input.
* @throws EncodingException For invalid encoding.
*/
public final void
wireDecode(ByteBuffer input, WireFormat wireFormat) throws EncodingException
{
wireFormat.decodeName(this, input, true);
}
/**
* Decode the input using the default wire format
* WireFormat.getDefaultWireFormat() and update this Name.
* @param input The input buffer to decode. This reads from position() to
* limit(), but does not change the position.
* @throws EncodingException For invalid encoding.
*/
public final void
wireDecode(ByteBuffer input) throws EncodingException
{
wireDecode(input, WireFormat.getDefaultWireFormat());
}
/**
* Decode the input using a particular wire format and update this Name.
* @param input The input blob to decode.
* @param wireFormat A WireFormat object used to decode the input.
* @throws EncodingException For invalid encoding.
*/
public final void
wireDecode(Blob input, WireFormat wireFormat) throws EncodingException
{
wireFormat.decodeName(this, input.buf(), false);
}
/**
* Decode the input using the default wire format
* WireFormat.getDefaultWireFormat() and update this Name.
* @param input The input blob to decode.
* @throws EncodingException For invalid encoding.
*/
public final void
wireDecode(Blob input) throws EncodingException
{
wireDecode(input, WireFormat.getDefaultWireFormat());
}
/**
* Compare this to the other Name using NDN canonical ordering. If the first
* components of each name are not equal, this returns -1 if the first comes
* before the second using the NDN canonical ordering for name components, or
* 1 if it comes after. If they are equal, this compares the second components
* of each name, etc. If both names are the same up to the size of the
* shorter name, this returns -1 if the first name is shorter than the second
* or 1 if it is longer. For example, sorted gives:
* /a/b/d /a/b/cc /c /c/a /bb . This is intuitive because all names with the
* prefix /a are next to each other. But it may be also be counter-intuitive
* because /c comes before /bb according to NDN canonical ordering since it is
* shorter.
* @param other The other Name to compare with.
* @return 0 If they compare equal, -1 if this Name comes before other in the
* canonical ordering, or 1 if this Name comes after other in the canonical
* ordering.
*
* See http://named-data.net/doc/0.2/technical/CanonicalOrder.html
*/
public final int
compare(Name other)
{
return compare(0, components_.size(), other);
}
/**
* Compare a subset of this name to a subset of the other name, equivalent to
* this.getSubName(iStartComponent, nComponents).compare
* (other.getSubName(iOtherStartComponent, nOtherComponents)).
* @param iStartComponent The index if the first component of this name to
* compare. If iStartComponent is -N then compare components starting from
* name.size() - N.
* @param nComponents The number of components starting at iStartComponent.
* If greater than the size of this name, compare until the end of the name.
* @param other The other Name to compare with.
* @param iOtherStartComponent The index if the first component of the other
* name to compare. If iOtherStartComponent is -N then compare components
* starting from other.size() - N.
* @param nOtherComponents The number of components starting at
* iOtherStartComponent. If greater than the size of the other name, compare
* until the end of the name.
* @return 0 If the sub names compare equal, -1 if this sub name comes before
* the other sub name in the canonical ordering, or 1 if after.
*/
public final int
compare
(int iStartComponent, int nComponents, Name other,
int iOtherStartComponent, int nOtherComponents)
{
if (iStartComponent < 0)
iStartComponent = size() - (-iStartComponent);
if (iOtherStartComponent < 0)
iOtherStartComponent = other.size() - (-iOtherStartComponent);
nComponents = Math.min(nComponents, size() - iStartComponent);
nOtherComponents = Math.min(nOtherComponents, other.size() - iOtherStartComponent);
int count = Math.min(nComponents, nOtherComponents);
for (int i = 0; i < count; ++i) {
int comparison = components_.get(iStartComponent + i).compare
(other.components_.get(iOtherStartComponent + i));
if (comparison == 0)
// The components at this index are equal, so check the next components.
continue;
// Otherwise, the result is based on the components at this index.
return comparison;
}
// The components up to min(this.size(), other.size()) are equal, so the
// shorter name is less.
if (nComponents < nOtherComponents)
return -1;
else if (nComponents > nOtherComponents)
return 1;
else
return 0;
}
/**
* Compare a subset of this name to a subset of the other name, equivalent to
* this.getSubName(iStartComponent, nComponents).compare
* (other.getSubName(iOtherStartComponent)), getting all components of other
* from iOtherStartComponent to the end of the name.
* @param iStartComponent The index if the first component of this name to
* compare. If iStartComponent is -N then compare components starting from
* name.size() - N.
* @param nComponents The number of components starting at iStartComponent.
* If greater than the size of this name, compare until the end of the name.
* @param other The other Name to compare with.
* @param iOtherStartComponent The index if the first component of the other
* name to compare. If iOtherStartComponent is -N then compare components
* starting from other.size() - N.
* @return 0 If the sub names compare equal, -1 if this sub name comes before
* the other sub name in the canonical ordering, or 1 if after.
*/
public final int
compare
(int iStartComponent, int nComponents, Name other,
int iOtherStartComponent)
{
return compare
(iStartComponent, nComponents, other, iOtherStartComponent,
other.components_.size());
}
/**
* Compare a subset of this name to all of the other name, equivalent to
* this.getSubName(iStartComponent, nComponents).compare(other).
* @param iStartComponent The index if the first component of this name to
* compare. If iStartComponent is -N then compare components starting from
* name.size() - N.
* @param nComponents The number of components starting at iStartComponent.
* If greater than the size of this name, compare until the end of the name.
* @param other The other Name to compare with.
* @return 0 If the sub names compare equal, -1 if this sub name comes before
* the other name in the canonical ordering, or 1 if after.
*/
public final int
compare
(int iStartComponent, int nComponents, Name other)
{
return compare
(iStartComponent, nComponents, other, 0, other.components_.size());
}
public final int
compareTo(Object o) { return this.compare((Name)o); }
// Also include this version for portability.
public final int
CompareTo(Object o) { return this.compare((Name)o); }
/**
* Get the change count, which is incremented each time this object is changed.
* @return The change count.
*/
public final long
getChangeCount() { return changeCount_; }
/**
* Make a Blob value by decoding the escapedString between beginOffset and
* endOffset according to the NDN URI Scheme. If the escaped string is
* "", "." or ".." then return a Blob with a null pointer, which means the
* component should be skipped in a URI name.
* This does not check for a type code prefix such as "sha256digest=".
* @param escapedString The escaped string
* @param beginOffset The offset in escapedString of the beginning of the
* portion to decode.
* @param endOffset The offset in escapedString of the end of the portion to
* decode.
* @return The Blob value. If the escapedString is not a valid escaped
* component, then the Blob has a null pointer.
*/
public static Blob
fromEscapedString(String escapedString, int beginOffset, int endOffset)
{
String trimmedString =
escapedString.substring(beginOffset, endOffset).trim();
ByteBuffer value = unescape(trimmedString);
// Check for all dots.
boolean gotNonDot = false;
for (int i = value.position(); i < value.limit(); ++i) {
if (value.get(i) != '.') {
gotNonDot = true;
break;
}
}
if (!gotNonDot) {
// Special case for component of only periods.
if (value.remaining() <= 2)
// Zero, one or two periods is illegal. Ignore this component.
return new Blob();
else {
// Remove 3 periods.
value.position(value.position() + 3);
return new Blob(value, false);
}
}
else
return new Blob(value, false);
}
/**
* Make a Blob value by decoding the escapedString according to the NDN URI
* Scheme.
* If the escaped string is "", "." or ".." then return a Blob with a null
* pointer, which means the component should be skipped in a URI name.
* This does not check for a type code prefix such as "sha256digest=".
* @param escapedString The escaped string.
* @return The Blob value. If the escapedString is not a valid escaped
* component, then the Blob has a null pointer.
*/
public static Blob
fromEscapedString(String escapedString)
{
return fromEscapedString(escapedString, 0, escapedString.length());
}
/**
* Write the value to result, escaping characters according to the NDN URI
* Scheme.
* This also adds "..." to a value with zero or more ".".
* This does not add a type code prefix such as "sha256digest=".
* @param value The ByteBuffer with the value. This reads from position() to
* limit().
* @param result The StringBuffer to write to.
*/
public static void
toEscapedString(ByteBuffer value, StringBuffer result)
{
boolean gotNonDot = false;
for (int i = value.position(); i < value.limit(); ++i) {
if (value.get(i) != 0x2e) {
gotNonDot = true;
break;
}
}
if (!gotNonDot) {
// Special case for component of zero or more periods. Add 3 periods.
result.append("...");
for (int i = value.position(); i < value.limit(); ++i)
result.append('.');
}
else {
for (int i = value.position(); i < value.limit(); ++i) {
int x = ((int)value.get(i) & 0xff);
// Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
if (x >= 0x30 && x <= 0x39 || x >= 0x41 && x <= 0x5a ||
x >= 0x61 && x <= 0x7a || x == 0x2b || x == 0x2d ||
x == 0x2e || x == 0x5f)
result.append((char)x);
else {
result.append('%');
if (x < 16)
result.append('0');
result.append(Integer.toHexString(x).toUpperCase());
}
}
}
}
/**
* Convert the value by escaping characters according to the NDN URI Scheme.
* This also adds "..." to a value with zero or more ".".
* This does not add a type code prefix such as "sha256digest=".
* @param value The ByteBuffer with the value. This reads from position() to
* limit().
* @return The escaped string.
*/
public static String
toEscapedString(ByteBuffer value)
{
StringBuffer result = new StringBuffer(value.remaining());
toEscapedString(value, result);
return result.toString();
}
/**
* Make a Blob value by decoding the hexString between beginOffset and
* endOffset.
* @param hexString The hex string.
* @param beginOffset The offset in hexString of the beginning of the
* portion to decode.
* @param endOffset The offset in hexString of the end of the portion to
* decode.
* @return The Blob value. If the hexString is not a valid hex string, then
* the Blob has a null pointer.
*/
public static Blob
fromHex(String hexString, int beginOffset, int endOffset)
{
ByteBuffer result = ByteBuffer.allocate((endOffset - beginOffset) / 2);
for (int i = beginOffset; i < endOffset; ++i) {
if (hexString.charAt(i) == ' ')
// Skip whitespace.
continue;
if (i + 1 >= endOffset)
// Only one hex digit. Ignore.
break;
int hi = fromHexChar(hexString.charAt(i));
int lo = fromHexChar(hexString.charAt(i + 1));
if (hi < 0 || lo < 0)
// Invalid hex characters.
return new Blob();
result.put((byte)(16 * hi + lo));
// Skip past the second digit.
i += 1;
}
result.flip();
return new Blob(result, false);
}
/**
* Convert the hex character to an integer from 0 to 15.
* @param c The hex character.
* @return The hex value, or -1 if not a hex character.
*/
private static int
fromHexChar(char c)
{
if (c >= '0' && c <= '9')
return (int)c - (int)'0';
else if (c >= 'A' && c <= 'F')
return (int)c - (int)'A' + 10;
else if (c >= 'a' && c <= 'f')
return (int)c - (int)'a' + 10;
else
return -1;
}
/**
* Return a copy of str, converting each escaped "%XX" to the char value.
* @param str The escaped string.
* @return The unescaped string as a ByteBuffer with position and limit set.
*/
private static ByteBuffer
unescape(String str)
{
// We know the result will be shorter than the input str.
ByteBuffer result = ByteBuffer.allocate(str.length());
for (int i = 0; i < str.length(); ++i) {
if (str.charAt(i) == '%' && i + 2 < str.length()) {
int hi = fromHexChar(str.charAt(i + 1));
int lo = fromHexChar(str.charAt(i + 2));
if (hi < 0 || lo < 0)
// Invalid hex characters, so just keep the escaped string.
result.put((byte)str.charAt(i)).put((byte)str.charAt(i + 1)).put
((byte)str.charAt(i + 2));
else
result.put((byte)(16 * hi + lo));
// Skip ahead past the escaped value.
i += 2;
}
else
// Just copy through.
result.put((byte)str.charAt(i));
}
result.flip();
return result;
}
private final ArrayList components_;
private long changeCount_ = 0;
private boolean haveHashCode_ = false;
private int hashCode_;
private long hashCodeChangeCount_ = 0;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy