
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-android-with-async-io Show documentation
Show all versions of jndn-android-with-async-io 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-2017 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