com.google.cloud.storage.BlobInfo Maven / Gradle / Ivy
Show all versions of google-cloud-storage Show documentation
/*
* Copyright 2015 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.cloud.storage;
import static com.google.cloud.storage.BackwardCompatibilityUtils.millisOffsetDateTimeCodec;
import static com.google.cloud.storage.Utils.diffMaps;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.api.client.util.Data;
import com.google.api.core.ApiFunction;
import com.google.api.core.BetaApi;
import com.google.cloud.StringEnumType;
import com.google.cloud.StringEnumValue;
import com.google.cloud.storage.Storage.BlobField;
import com.google.cloud.storage.TransportCompatibility.Transport;
import com.google.cloud.storage.UnifiedOpts.NamedField;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.BaseEncoding;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.time.OffsetDateTime;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Information about an object in Google Cloud Storage. A {@code BlobInfo} object includes the
* {@code BlobId} instance and the set of properties, such as the blob's access control
* configuration, user provided metadata, the CRC32C checksum, etc. Instances of this class are used
* to create a new object in Google Cloud Storage or update the properties of an existing object. To
* deal with existing Storage objects the API includes the {@link Blob} class which extends {@code
* BlobInfo} and declares methods to perform operations on the object. Neither {@code BlobInfo} nor
* {@code Blob} instances keep the object content, just the object properties.
*
* Example of usage {@code BlobInfo} to create an object in Google Cloud Storage:
*
*
{@code
* BlobId blobId = BlobId.of(bucketName, blobName);
* BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("text/plain").build();
* Blob blob = storage.create(blobInfo, "Hello, world".getBytes(StandardCharsets.UTF_8));
* }
*
* @see Concepts and
* Terminology
*/
@TransportCompatibility({Transport.HTTP, Transport.GRPC})
public class BlobInfo implements Serializable {
private static final long serialVersionUID = -2490471217826624578L;
private final BlobId blobId;
private final String generatedId;
private final String selfLink;
private final String cacheControl;
private final List acl;
private final Acl.Entity owner;
private final Long size;
private final String etag;
private final String md5;
private final String crc32c;
private final OffsetDateTime customTime;
private final String mediaLink;
/**
* The getter for this property never returns null, however null awareness is critical for
* encoding
*
* @see JsonConversions#blobInfo() encoder
*/
final Map metadata;
private final Long metageneration;
private final OffsetDateTime deleteTime;
private final OffsetDateTime updateTime;
private final OffsetDateTime createTime;
private final String contentType;
private final String contentEncoding;
private final String contentDisposition;
private final String contentLanguage;
private final StorageClass storageClass;
private final OffsetDateTime timeStorageClassUpdated;
private final Integer componentCount;
private final boolean isDirectory;
private final CustomerEncryption customerEncryption;
private final String kmsKeyName;
private final Boolean eventBasedHold;
private final Boolean temporaryHold;
private final OffsetDateTime retentionExpirationTime;
private final Retention retention;
private final transient ImmutableSet modifiedFields;
/** This class is meant for internal use only. Users are discouraged from using this class. */
public static final class ImmutableEmptyMap extends AbstractMap {
@Override
public Set> entrySet() {
return ImmutableSet.of();
}
}
/**
* Objects of this class hold information on the customer-supplied encryption key, if the blob is
* encrypted using such a key.
*/
public static class CustomerEncryption implements Serializable {
private static final long serialVersionUID = -7427738060808591323L;
private final String encryptionAlgorithm;
private final String keySha256;
CustomerEncryption(String encryptionAlgorithm, String keySha256) {
this.encryptionAlgorithm = encryptionAlgorithm;
this.keySha256 = keySha256;
}
/** Returns the algorithm used to encrypt the blob. */
public String getEncryptionAlgorithm() {
return encryptionAlgorithm;
}
/** Returns the SHA256 hash of the encryption key. */
public String getKeySha256() {
return keySha256;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("encryptionAlgorithm", getEncryptionAlgorithm())
.add("keySha256", getKeySha256())
.toString();
}
@Override
public final int hashCode() {
return Objects.hash(encryptionAlgorithm, keySha256);
}
@Override
public final boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CustomerEncryption)) {
return false;
}
CustomerEncryption that = (CustomerEncryption) o;
return Objects.equals(encryptionAlgorithm, that.encryptionAlgorithm)
&& Objects.equals(keySha256, that.keySha256);
}
}
/**
* Defines a blob's Retention policy. Can only be used on objects in a retention-enabled bucket.
*/
public static final class Retention implements Serializable {
private static final long serialVersionUID = 5046718464542688444L;
private Mode mode;
private OffsetDateTime retainUntilTime;
/** Returns the retention policy's Mode. Can be Locked or Unlocked. */
public Mode getMode() {
return mode;
}
/** Returns what time this object will be retained until, if the mode is Locked. */
public OffsetDateTime getRetainUntilTime() {
return retainUntilTime;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Retention)) {
return false;
}
Retention that = (Retention) o;
return Objects.equals(mode, that.mode)
&& Objects.equals(retainUntilTime, that.retainUntilTime);
}
@Override
public int hashCode() {
return Objects.hash(mode, retainUntilTime);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("mode", mode)
.add("retainUntilTime", retainUntilTime)
.toString();
}
public static Builder newBuilder() {
return new Builder();
}
public Builder toBuilder() {
return new Builder().setMode(this.mode).setRetainUntilTime(this.retainUntilTime);
}
private Retention() {}
public Retention(Builder builder) {
this.mode = builder.mode;
this.retainUntilTime = builder.retainUntilTime;
}
public static final class Builder {
private Mode mode;
private OffsetDateTime retainUntilTime;
/** Sets the retention policy's Mode. Can be Locked or Unlocked. */
public Builder setMode(Mode mode) {
this.mode = mode;
return this;
}
/** Sets what time this object will be retained until, if the mode is Locked. */
public Builder setRetainUntilTime(OffsetDateTime retainUntilTime) {
this.retainUntilTime = retainUntilTime;
return this;
}
public Retention build() {
return new Retention(this);
}
}
public static final class Mode extends StringEnumValue {
private static final long serialVersionUID = 1973143582659557184L;
private Mode(String constant) {
super(constant);
}
private static final ApiFunction CONSTRUCTOR = Mode::new;
private static final StringEnumType type =
new StringEnumType<>(Mode.class, CONSTRUCTOR);
public static final Mode UNLOCKED = type.createAndRegister("Unlocked");
public static final Mode LOCKED = type.createAndRegister("Locked");
public static Mode valueOfStrict(String constant) {
return type.valueOfStrict(constant);
}
public static Mode valueOf(String constant) {
return type.valueOf(constant);
}
public static Mode[] values() {
return type.values();
}
}
}
/** Builder for {@code BlobInfo}. */
public abstract static class Builder {
/** Sets the blob identity. */
public abstract Builder setBlobId(BlobId blobId);
abstract Builder setGeneratedId(String generatedId);
/**
* Sets the blob's data content type.
*
* @see Content-Type
*/
public abstract Builder setContentType(String contentType);
/**
* Sets the blob's data content disposition.
*
* @see Content-Disposition
*/
public abstract Builder setContentDisposition(String contentDisposition);
/**
* Sets the blob's data content language.
*
* @see Content-Language
*/
public abstract Builder setContentLanguage(String contentLanguage);
/**
* Sets the blob's data content encoding.
*
* @see Content-Encoding
*/
public abstract Builder setContentEncoding(String contentEncoding);
abstract Builder setComponentCount(Integer componentCount);
/**
* Sets the blob's data cache control.
*
* @see Cache-Control
*/
public abstract Builder setCacheControl(String cacheControl);
/**
* Sets the blob's access control configuration.
*
* @see
* About Access Control Lists
*/
public abstract Builder setAcl(List acl);
abstract Builder setOwner(Acl.Entity owner);
abstract Builder setSize(Long size);
abstract Builder setEtag(String etag);
abstract Builder setSelfLink(String selfLink);
/**
* Sets the MD5 hash of blob's data. MD5 value must be encoded in base64.
*
* @see Hashes and ETags:
* Best Practices
*/
public abstract Builder setMd5(String md5);
/**
* Sets the MD5 hash of blob's data from hex string.
*
* @see Hashes and ETags:
* Best Practices
* @throws IllegalArgumentException when given an invalid hexadecimal value.
*/
public abstract Builder setMd5FromHexString(String md5HexString);
/**
* Sets the CRC32C checksum of blob's data as described in RFC 4960, Appendix B; encoded in
* base64 in big-endian order.
*
* @see Hashes and ETags:
* Best Practices
*/
public abstract Builder setCrc32c(String crc32c);
/**
* Sets the custom time for an object. Once set it can't be unset and only changed to a custom
* datetime in the future. To unset the custom time, you must either perform a rewrite operation
* or upload the data again.
*
* Example of setting the custom time.
*
*
{@code
* String bucketName = "my-unique-bucket";
* String blobName = "my-blob-name";
* long customTime = 1598423868301L;
* BlobInfo blob = BlobInfo.newBuilder(bucketName, blobName).setCustomTime(customTime).build();
* }
*
* @deprecated Use {@link #setCustomTimeOffsetDateTime(OffsetDateTime)}
*/
@Deprecated
public Builder setCustomTime(Long customTime) {
throw new UnsupportedOperationException(
"Override setCustomTime with your own implementation,"
+ " or use com.google.cloud.storage.Blob.");
}
/**
* Sets the custom time for an object. Once set it can't be unset and only changed to a custom
* datetime in the future. To unset the custom time, you must either perform a rewrite operation
* or upload the data again.
*
* Example of setting the custom time.
*
*
{@code
* String bucketName = "my-unique-bucket";
* String blobName = "my-blob-name";
* OffsetDateTime customTime = Instant.ofEpochMilli(1598423868301L).atOffset(0); // UTC
* BlobInfo blob = BlobInfo.newBuilder(bucketName, blobName).setCustomTime(customTime).build();
* }
*/
public Builder setCustomTimeOffsetDateTime(OffsetDateTime customTime) {
// provide an implementation for source and binary compatibility which we override ourselves
return setCustomTime(millisOffsetDateTimeCodec.decode(customTime));
}
/**
* Sets the CRC32C checksum of blob's data as described in RFC 4960, Appendix B; from hex
* string.
*
* @see Hashes and ETags:
* Best Practices
* @throws IllegalArgumentException when given an invalid hexadecimal value.
*/
public abstract Builder setCrc32cFromHexString(String crc32cHexString);
abstract Builder setMediaLink(String mediaLink);
/** Sets the blob's storage class. */
public abstract Builder setStorageClass(StorageClass storageClass);
/**
* Sets the modification time of an object's storage class. Once set it can't be unset directly,
* the only way is to rewrite the object with the desired storage class.
*
* @deprecated Use {@link #setTimeStorageClassUpdatedOffsetDateTime(OffsetDateTime)}
*/
@Deprecated
public Builder setTimeStorageClassUpdated(Long timeStorageClassUpdated) {
throw new UnsupportedOperationException(
"Override setTimeStorageClassUpdated with your own implementation,"
+ " or use com.google.cloud.storage.Blob.");
}
public Builder setTimeStorageClassUpdatedOffsetDateTime(
OffsetDateTime timeStorageClassUpdated) {
// provide an implementation for source and binary compatibility which we override ourselves
return setTimeStorageClassUpdated(millisOffsetDateTimeCodec.decode(timeStorageClassUpdated));
}
/** Sets the blob's user provided metadata. */
public abstract Builder setMetadata(@Nullable Map<@NonNull String, @Nullable String> metadata);
abstract Builder setMetageneration(Long metageneration);
/** @deprecated Use {@link #setDeleteTimeOffsetDateTime(OffsetDateTime)} */
@Deprecated
abstract Builder setDeleteTime(Long deleteTime);
Builder setDeleteTimeOffsetDateTime(OffsetDateTime deleteTime) {
// provide an implementation for source and binary compatibility which we override ourselves
return setDeleteTime(millisOffsetDateTimeCodec.decode(deleteTime));
}
/** @deprecated Use {@link #setUpdateTimeOffsetDateTime(OffsetDateTime)} */
@Deprecated
abstract Builder setUpdateTime(Long updateTime);
Builder setUpdateTimeOffsetDateTime(OffsetDateTime updateTime) {
// provide an implementation for source and binary compatibility which we override ourselves
return setUpdateTime(millisOffsetDateTimeCodec.decode(updateTime));
}
/** @deprecated Use {@link #setCreateTimeOffsetDateTime(OffsetDateTime)} */
@Deprecated
abstract Builder setCreateTime(Long createTime);
Builder setCreateTimeOffsetDateTime(OffsetDateTime createTime) {
// provide an implementation for source and binary compatibility which we override ourselves
return setCreateTime(millisOffsetDateTimeCodec.decode(createTime));
}
abstract Builder setIsDirectory(boolean isDirectory);
abstract Builder setCustomerEncryption(CustomerEncryption customerEncryption);
/**
* Sets a customer-managed key for server-side encryption of the blob. Note that when a KMS key
* is used to encrypt Cloud Storage object, object resource metadata will store the version of
* the KMS cryptographic. If a {@code Blob} with KMS Key metadata is used to upload a new
* version of the object then the existing kmsKeyName version value can't be used in the upload
* request and the client instead ignores it.
*
* Example of setting the KMS key name
*
*
{@code
* String bucketName = "my-unique-bucket";
* String blobName = "my-blob-name";
* String kmsKeyName = "projects/project-id/locations/us/keyRings/lab1/cryptoKeys/test-key"
* BlobInfo blobInfo = BlobInfo.newBuilder(bucketName, blobName).build();
* Blob blob = storage.create(blobInfo, Storage.BlobTargetOption.kmsKeyName(kmsKeyName));
* }
*/
abstract Builder setKmsKeyName(String kmsKeyName);
/** Sets the blob's event-based hold. */
@BetaApi
public abstract Builder setEventBasedHold(Boolean eventBasedHold);
/** Sets the blob's temporary hold. */
@BetaApi
public abstract Builder setTemporaryHold(Boolean temporaryHold);
/** @deprecated {@link #setRetentionExpirationTimeOffsetDateTime(OffsetDateTime)} */
@BetaApi
@Deprecated
abstract Builder setRetentionExpirationTime(Long retentionExpirationTime);
@BetaApi
Builder setRetentionExpirationTimeOffsetDateTime(OffsetDateTime retentionExpirationTime) {
// provide an implementation for source and binary compatibility which we override ourselves
return setRetentionExpirationTime(millisOffsetDateTimeCodec.decode(retentionExpirationTime));
}
public abstract Builder setRetention(Retention retention);
/** Creates a {@code BlobInfo} object. */
public abstract BlobInfo build();
abstract BlobId getBlobId();
abstract Builder clearBlobId();
abstract Builder clearGeneratedId();
abstract Builder clearContentType();
abstract Builder clearContentEncoding();
abstract Builder clearContentDisposition();
abstract Builder clearContentLanguage();
abstract Builder clearComponentCount();
abstract Builder clearCacheControl();
abstract Builder clearAcl();
abstract Builder clearOwner();
abstract Builder clearSize();
abstract Builder clearEtag();
abstract Builder clearSelfLink();
abstract Builder clearMd5();
abstract Builder clearCrc32c();
abstract Builder clearCustomTime();
abstract Builder clearMediaLink();
abstract Builder clearMetadata();
abstract Builder clearMetageneration();
abstract Builder clearDeleteTime();
abstract Builder clearUpdateTime();
abstract Builder clearCreateTime();
abstract Builder clearIsDirectory();
abstract Builder clearCustomerEncryption();
abstract Builder clearStorageClass();
abstract Builder clearTimeStorageClassUpdated();
abstract Builder clearKmsKeyName();
abstract Builder clearEventBasedHold();
abstract Builder clearTemporaryHold();
abstract Builder clearRetentionExpirationTime();
}
static final class BuilderImpl extends Builder {
private static final String hexDecimalValues = "0123456789abcdef";
private BlobId blobId;
private String generatedId;
private String contentType;
private String contentEncoding;
private String contentDisposition;
private String contentLanguage;
private Integer componentCount;
private String cacheControl;
private List acl;
private Acl.Entity owner;
private Long size;
private String etag;
private String selfLink;
private String md5;
private String crc32c;
private OffsetDateTime customTime;
private String mediaLink;
private Map metadata;
private Long metageneration;
private OffsetDateTime deleteTime;
private OffsetDateTime updateTime;
private OffsetDateTime createTime;
private Boolean isDirectory;
private CustomerEncryption customerEncryption;
private StorageClass storageClass;
private OffsetDateTime timeStorageClassUpdated;
private String kmsKeyName;
private Boolean eventBasedHold;
private Boolean temporaryHold;
private OffsetDateTime retentionExpirationTime;
private Retention retention;
private final ImmutableSet.Builder modifiedFields = ImmutableSet.builder();
BuilderImpl(BlobId blobId) {
this.blobId = blobId;
}
BuilderImpl(BlobInfo blobInfo) {
blobId = blobInfo.blobId;
generatedId = blobInfo.generatedId;
cacheControl = blobInfo.cacheControl;
contentEncoding = blobInfo.contentEncoding;
contentType = blobInfo.contentType;
contentDisposition = blobInfo.contentDisposition;
contentLanguage = blobInfo.contentLanguage;
componentCount = blobInfo.componentCount;
customerEncryption = blobInfo.customerEncryption;
acl = blobInfo.acl;
owner = blobInfo.owner;
size = blobInfo.size;
etag = blobInfo.etag;
selfLink = blobInfo.selfLink;
md5 = blobInfo.md5;
crc32c = blobInfo.crc32c;
customTime = blobInfo.customTime;
mediaLink = blobInfo.mediaLink;
metadata = blobInfo.metadata;
metageneration = blobInfo.metageneration;
deleteTime = blobInfo.deleteTime;
updateTime = blobInfo.updateTime;
createTime = blobInfo.createTime;
isDirectory = blobInfo.isDirectory;
storageClass = blobInfo.storageClass;
timeStorageClassUpdated = blobInfo.timeStorageClassUpdated;
kmsKeyName = blobInfo.kmsKeyName;
eventBasedHold = blobInfo.eventBasedHold;
temporaryHold = blobInfo.temporaryHold;
retentionExpirationTime = blobInfo.retentionExpirationTime;
retention = blobInfo.retention;
}
@Override
public Builder setBlobId(BlobId blobId) {
checkNotNull(blobId);
if (!Objects.equals(this.blobId, blobId)) {
if (!Objects.equals(this.blobId.getBucket(), blobId.getBucket())) {
modifiedFields.add(BlobField.BUCKET);
}
if (!Objects.equals(this.blobId.getName(), blobId.getName())) {
modifiedFields.add(BlobField.NAME);
}
if (!Objects.equals(this.blobId.getGeneration(), blobId.getGeneration())) {
modifiedFields.add(BlobField.GENERATION);
}
}
this.blobId = blobId;
return this;
}
@Override
Builder setGeneratedId(String generatedId) {
this.generatedId = generatedId;
return this;
}
@Override
public Builder setContentType(String contentType) {
String tmp = firstNonNull(contentType, Data.nullOf(String.class));
if (!Objects.equals(this.contentType, tmp)) {
modifiedFields.add(BlobField.CONTENT_TYPE);
}
this.contentType = tmp;
return this;
}
@Override
public Builder setContentDisposition(String contentDisposition) {
String tmp = firstNonNull(contentDisposition, Data.nullOf(String.class));
if (!Objects.equals(this.contentDisposition, tmp)) {
modifiedFields.add(BlobField.CONTENT_DISPOSITION);
}
this.contentDisposition = tmp;
return this;
}
@Override
public Builder setContentLanguage(String contentLanguage) {
String tmp = firstNonNull(contentLanguage, Data.nullOf(String.class));
if (!Objects.equals(this.contentLanguage, tmp)) {
modifiedFields.add(BlobField.CONTENT_LANGUAGE);
}
this.contentLanguage = tmp;
return this;
}
@Override
public Builder setContentEncoding(String contentEncoding) {
String tmp = firstNonNull(contentEncoding, Data.nullOf(String.class));
if (!Objects.equals(this.contentEncoding, tmp)) {
modifiedFields.add(BlobField.CONTENT_ENCODING);
}
this.contentEncoding = tmp;
return this;
}
@Override
Builder setComponentCount(Integer componentCount) {
this.componentCount = componentCount;
return this;
}
@Override
public Builder setCacheControl(String cacheControl) {
String tmp = firstNonNull(cacheControl, Data.nullOf(String.class));
if (!Objects.equals(this.cacheControl, tmp)) {
modifiedFields.add(BlobField.CACHE_CONTROL);
}
this.cacheControl = tmp;
return this;
}
@Override
public Builder setAcl(List acl) {
if (!Objects.equals(this.acl, acl)) {
modifiedFields.add(BlobField.ACL);
}
if (acl != null) {
if (acl instanceof ImmutableList) {
this.acl = acl;
} else {
this.acl = ImmutableList.copyOf(acl);
}
} else {
this.acl = null;
}
return this;
}
@Override
Builder setOwner(Acl.Entity owner) {
if (!Objects.equals(this.owner, owner)) {
modifiedFields.add(BlobField.OWNER);
}
this.owner = owner;
return this;
}
@Override
Builder setSize(Long size) {
this.size = size;
return this;
}
@Override
Builder setEtag(String etag) {
if (!Objects.equals(this.etag, etag)) {
modifiedFields.add(BlobField.ETAG);
}
this.etag = etag;
return this;
}
@Override
Builder setSelfLink(String selfLink) {
this.selfLink = selfLink;
return this;
}
@Override
public Builder setMd5(String md5) {
String tmp = firstNonNull(md5, Data.nullOf(String.class));
if (!Objects.equals(this.md5, tmp)) {
modifiedFields.add(BlobField.MD5HASH);
}
this.md5 = tmp;
return this;
}
@Override
public Builder setMd5FromHexString(String md5HexString) {
if (md5HexString == null) {
return this;
}
if (md5HexString.length() % 2 != 0) {
throw new IllegalArgumentException(
"each byte must be represented by 2 valid hexadecimal characters");
}
String md5HexStringLower = md5HexString.toLowerCase();
ByteBuffer md5ByteBuffer = ByteBuffer.allocate(md5HexStringLower.length() / 2);
for (int charIndex = 0; charIndex < md5HexStringLower.length(); charIndex += 2) {
int higherOrderBits = this.hexDecimalValues.indexOf(md5HexStringLower.charAt(charIndex));
int lowerOrderBits = this.hexDecimalValues.indexOf(md5HexStringLower.charAt(charIndex + 1));
if (higherOrderBits == -1 || lowerOrderBits == -1) {
throw new IllegalArgumentException(
"each byte must be represented by 2 valid hexadecimal characters");
}
md5ByteBuffer.put((byte) (higherOrderBits << 4 | lowerOrderBits));
}
return setMd5(BaseEncoding.base64().encode(md5ByteBuffer.array()));
}
@Override
public Builder setCrc32c(String crc32c) {
String tmp = firstNonNull(crc32c, Data.nullOf(String.class));
if (!Objects.equals(this.crc32c, tmp)) {
modifiedFields.add(BlobField.CRC32C);
}
this.crc32c = tmp;
return this;
}
/** @deprecated {@link #setCustomTimeOffsetDateTime(OffsetDateTime)} */
@Override
@Deprecated
public Builder setCustomTime(Long customTime) {
return setCustomTimeOffsetDateTime(millisOffsetDateTimeCodec.encode(customTime));
}
@Override
public Builder setCustomTimeOffsetDateTime(OffsetDateTime customTime) {
if (!Objects.equals(this.customTime, customTime)) {
modifiedFields.add(BlobField.CUSTOM_TIME);
}
this.customTime = customTime;
return this;
}
@Override
public Builder setCrc32cFromHexString(String crc32cHexString) {
if (crc32cHexString == null) {
return this;
}
if (crc32cHexString.length() % 2 != 0) {
throw new IllegalArgumentException(
"each byte must be represented by 2 valid hexadecimal characters");
}
String crc32cHexStringLower = crc32cHexString.toLowerCase();
ByteBuffer crc32cByteBuffer = ByteBuffer.allocate(crc32cHexStringLower.length() / 2);
for (int charIndex = 0; charIndex < crc32cHexStringLower.length(); charIndex += 2) {
int higherOrderBits = this.hexDecimalValues.indexOf(crc32cHexStringLower.charAt(charIndex));
int lowerOrderBits =
this.hexDecimalValues.indexOf(crc32cHexStringLower.charAt(charIndex + 1));
if (higherOrderBits == -1 || lowerOrderBits == -1) {
throw new IllegalArgumentException(
"each byte must be represented by 2 valid hexadecimal characters");
}
crc32cByteBuffer.put((byte) (higherOrderBits << 4 | lowerOrderBits));
}
return setCrc32c(BaseEncoding.base64().encode(crc32cByteBuffer.array()));
}
@Override
Builder setMediaLink(String mediaLink) {
this.mediaLink = mediaLink;
return this;
}
@SuppressWarnings({"UnnecessaryLocalVariable"})
@Override
public Builder setMetadata(@Nullable Map<@NonNull String, @Nullable String> metadata) {
Map left = this.metadata;
Map right = metadata;
if (!Objects.equals(left, right)) {
diffMaps(BlobField.METADATA, left, right, modifiedFields::add);
if (right != null) {
this.metadata = new HashMap<>(right);
} else {
this.metadata = (Map) Data.nullOf(ImmutableEmptyMap.class);
}
}
return this;
}
@Override
public Builder setStorageClass(StorageClass storageClass) {
if (!Objects.equals(this.storageClass, storageClass)) {
modifiedFields.add(BlobField.STORAGE_CLASS);
}
this.storageClass = storageClass;
return this;
}
/** @deprecated Use {@link #setTimeStorageClassUpdatedOffsetDateTime(OffsetDateTime)} */
@Deprecated
@Override
public Builder setTimeStorageClassUpdated(Long timeStorageClassUpdated) {
return setTimeStorageClassUpdatedOffsetDateTime(
millisOffsetDateTimeCodec.encode(timeStorageClassUpdated));
}
@Override
public Builder setTimeStorageClassUpdatedOffsetDateTime(
OffsetDateTime timeStorageClassUpdated) {
if (!Objects.equals(this.timeStorageClassUpdated, timeStorageClassUpdated)) {
modifiedFields.add(BlobField.TIME_STORAGE_CLASS_UPDATED);
}
this.timeStorageClassUpdated = timeStorageClassUpdated;
return this;
}
@Override
Builder setMetageneration(Long metageneration) {
this.metageneration = metageneration;
return this;
}
/** @deprecated Use {@link #setDeleteTimeOffsetDateTime(OffsetDateTime)} */
@Deprecated
@Override
Builder setDeleteTime(Long deleteTime) {
return setDeleteTimeOffsetDateTime(millisOffsetDateTimeCodec.encode(deleteTime));
}
@Override
Builder setDeleteTimeOffsetDateTime(OffsetDateTime deleteTime) {
if (!Objects.equals(this.deleteTime, deleteTime)) {
modifiedFields.add(BlobField.TIME_DELETED);
}
this.deleteTime = deleteTime;
return this;
}
/** @deprecated Use {@link #setUpdateTimeOffsetDateTime(OffsetDateTime)} */
@Override
Builder setUpdateTime(Long updateTime) {
return setUpdateTimeOffsetDateTime(millisOffsetDateTimeCodec.encode(updateTime));
}
@Override
Builder setUpdateTimeOffsetDateTime(OffsetDateTime updateTime) {
if (!Objects.equals(this.updateTime, updateTime)) {
modifiedFields.add(BlobField.UPDATED);
}
this.updateTime = updateTime;
return this;
}
/** @deprecated Use {@link #setCreateTimeOffsetDateTime(OffsetDateTime)} */
@Deprecated
@Override
Builder setCreateTime(Long createTime) {
return setCreateTimeOffsetDateTime(millisOffsetDateTimeCodec.encode(createTime));
}
@Override
Builder setCreateTimeOffsetDateTime(OffsetDateTime createTime) {
if (!Objects.equals(this.createTime, createTime)) {
modifiedFields.add(BlobField.TIME_CREATED);
}
this.createTime = createTime;
return this;
}
@Override
Builder setIsDirectory(boolean isDirectory) {
this.isDirectory = isDirectory;
return this;
}
@Override
Builder setCustomerEncryption(CustomerEncryption customerEncryption) {
if (!Objects.equals(this.customerEncryption, customerEncryption)) {
modifiedFields.add(BlobField.CUSTOMER_ENCRYPTION);
}
this.customerEncryption = customerEncryption;
return this;
}
@Override
Builder setKmsKeyName(String kmsKeyName) {
if (!Objects.equals(this.kmsKeyName, kmsKeyName)) {
modifiedFields.add(BlobField.KMS_KEY_NAME);
}
this.kmsKeyName = kmsKeyName;
return this;
}
@Override
public Builder setEventBasedHold(Boolean eventBasedHold) {
if (!Objects.equals(this.eventBasedHold, eventBasedHold)) {
modifiedFields.add(BlobField.EVENT_BASED_HOLD);
}
this.eventBasedHold = eventBasedHold;
return this;
}
@Override
public Builder setTemporaryHold(Boolean temporaryHold) {
if (!Objects.equals(this.temporaryHold, temporaryHold)) {
modifiedFields.add(BlobField.TEMPORARY_HOLD);
}
this.temporaryHold = temporaryHold;
return this;
}
/** @deprecated {@link #setRetentionExpirationTimeOffsetDateTime(OffsetDateTime)} */
@Override
@Deprecated
Builder setRetentionExpirationTime(Long retentionExpirationTime) {
return setRetentionExpirationTimeOffsetDateTime(
millisOffsetDateTimeCodec.encode(retentionExpirationTime));
}
@Override
Builder setRetentionExpirationTimeOffsetDateTime(OffsetDateTime retentionExpirationTime) {
if (!Objects.equals(this.retentionExpirationTime, retentionExpirationTime)) {
modifiedFields.add(BlobField.RETENTION_EXPIRATION_TIME);
}
this.retentionExpirationTime = retentionExpirationTime;
return this;
}
@Override
public Builder setRetention(Retention retention) {
// todo: b/308194853
modifiedFields.add(BlobField.RETENTION);
this.retention = retention;
return this;
}
@Override
public BlobInfo build() {
checkNotNull(blobId);
return new BlobInfo(this);
}
@Override
BlobId getBlobId() {
return blobId;
}
@Override
Builder clearBlobId() {
this.blobId = null;
return this;
}
@Override
Builder clearGeneratedId() {
this.generatedId = null;
return this;
}
@Override
Builder clearContentType() {
this.contentType = null;
return this;
}
@Override
Builder clearContentEncoding() {
this.contentEncoding = null;
return this;
}
@Override
Builder clearContentDisposition() {
this.contentDisposition = null;
return this;
}
@Override
Builder clearContentLanguage() {
this.contentLanguage = null;
return this;
}
@Override
Builder clearComponentCount() {
this.componentCount = null;
return this;
}
@Override
Builder clearCacheControl() {
this.cacheControl = null;
return this;
}
@Override
Builder clearAcl() {
this.acl = null;
return this;
}
@Override
Builder clearOwner() {
this.owner = null;
return this;
}
@Override
Builder clearSize() {
this.size = null;
return this;
}
@Override
Builder clearEtag() {
this.etag = null;
return this;
}
@Override
Builder clearSelfLink() {
this.selfLink = null;
return this;
}
@Override
Builder clearMd5() {
this.md5 = null;
return this;
}
@Override
Builder clearCrc32c() {
this.crc32c = null;
return this;
}
@Override
Builder clearCustomTime() {
this.customTime = null;
return this;
}
@Override
Builder clearMediaLink() {
this.mediaLink = null;
return this;
}
@Override
Builder clearMetadata() {
this.metadata = null;
return this;
}
@Override
Builder clearMetageneration() {
this.metageneration = null;
return this;
}
@Override
Builder clearDeleteTime() {
this.deleteTime = null;
return this;
}
@Override
Builder clearUpdateTime() {
this.updateTime = null;
return this;
}
@Override
Builder clearCreateTime() {
this.createTime = null;
return this;
}
@Override
Builder clearIsDirectory() {
this.isDirectory = null;
return this;
}
@Override
Builder clearCustomerEncryption() {
this.customerEncryption = null;
return this;
}
@Override
Builder clearStorageClass() {
this.storageClass = null;
return this;
}
@Override
Builder clearTimeStorageClassUpdated() {
this.timeStorageClassUpdated = null;
return this;
}
@Override
Builder clearKmsKeyName() {
this.kmsKeyName = null;
return this;
}
@Override
Builder clearEventBasedHold() {
this.eventBasedHold = null;
return this;
}
@Override
Builder clearTemporaryHold() {
this.temporaryHold = null;
return this;
}
@Override
Builder clearRetentionExpirationTime() {
this.retentionExpirationTime = null;
return this;
}
}
BlobInfo(BuilderImpl builder) {
blobId = builder.blobId;
generatedId = builder.generatedId;
cacheControl = builder.cacheControl;
contentEncoding = builder.contentEncoding;
contentType = builder.contentType;
contentDisposition = builder.contentDisposition;
contentLanguage = builder.contentLanguage;
componentCount = builder.componentCount;
customerEncryption = builder.customerEncryption;
acl = builder.acl;
owner = builder.owner;
size = builder.size;
etag = builder.etag;
selfLink = builder.selfLink;
md5 = builder.md5;
crc32c = builder.crc32c;
customTime = builder.customTime;
mediaLink = builder.mediaLink;
metadata = builder.metadata;
metageneration = builder.metageneration;
deleteTime = builder.deleteTime;
updateTime = builder.updateTime;
createTime = builder.createTime;
isDirectory = firstNonNull(builder.isDirectory, Boolean.FALSE);
storageClass = builder.storageClass;
timeStorageClassUpdated = builder.timeStorageClassUpdated;
kmsKeyName = builder.kmsKeyName;
eventBasedHold = builder.eventBasedHold;
temporaryHold = builder.temporaryHold;
retentionExpirationTime = builder.retentionExpirationTime;
retention = builder.retention;
modifiedFields = builder.modifiedFields.build();
}
/** Returns the blob's identity. */
public BlobId getBlobId() {
return blobId;
}
/** Returns the name of the containing bucket. */
public String getBucket() {
return getBlobId().getBucket();
}
/** Returns the service-generated for the blob. */
public String getGeneratedId() {
return generatedId;
}
/** Returns the blob's name. */
public String getName() {
return getBlobId().getName();
}
/**
* Returns the blob's data cache control.
*
* @see Cache-Control
*/
public String getCacheControl() {
return Data.isNull(cacheControl) ? null : cacheControl;
}
/**
* Returns the blob's access control configuration.
*
* @see
* About Access Control Lists
*/
public List getAcl() {
return acl;
}
/** Returns the blob's owner. This will always be the uploader of the blob. */
public Acl.Entity getOwner() {
return owner;
}
/**
* Returns the content length of the data in bytes.
*
* @see Content-Length
*/
public Long getSize() {
return size;
}
/**
* Returns the blob's data content type.
*
* @see Content-Type
*/
public String getContentType() {
return Data.isNull(contentType) ? null : contentType;
}
/**
* Returns the blob's data content encoding.
*
* @see Content-Encoding
*/
public String getContentEncoding() {
return Data.isNull(contentEncoding) ? null : contentEncoding;
}
/**
* Returns the blob's data content disposition.
*
* @see Content-Disposition
*/
public String getContentDisposition() {
return Data.isNull(contentDisposition) ? null : contentDisposition;
}
/**
* Returns the blob's data content language.
*
* @see Content-Language
*/
public String getContentLanguage() {
return Data.isNull(contentLanguage) ? null : contentLanguage;
}
/**
* Returns the number of components that make up this blob. Components are accumulated through the
* {@link Storage#compose(Storage.ComposeRequest)} operation and are limited to a count of 1024,
* counting 1 for each non-composite component blob and componentCount for each composite
* component blob. This value is set only for composite blobs.
*
* @see Component Count
* Property
*/
public Integer getComponentCount() {
return componentCount;
}
/**
* Returns HTTP 1.1 Entity tag for the blob.
*
* @see Entity Tags
*/
public String getEtag() {
return etag;
}
/** Returns the URI of this blob as a string. */
public String getSelfLink() {
return selfLink;
}
/**
* Returns the MD5 hash of blob's data encoded in base64.
*
* @see Hashes and ETags:
* Best Practices
*/
public String getMd5() {
return Data.isNull(md5) ? null : md5;
}
/**
* Returns the MD5 hash of blob's data decoded to string.
*
* @see Hashes and ETags:
* Best Practices
*/
public String getMd5ToHexString() {
if (md5 == null) {
return null;
}
byte[] decodedMd5 = BaseEncoding.base64().decode(md5);
StringBuilder stringBuilder = new StringBuilder();
for (byte b : decodedMd5) {
stringBuilder.append(String.format("%02x", b & 0xff));
}
return stringBuilder.toString();
}
/**
* Returns the CRC32C checksum of blob's data as described in RFC 4960, Appendix B; encoded in
* base64 in big-endian order.
*
* @see Hashes and ETags:
* Best Practices
*/
public String getCrc32c() {
return Data.isNull(crc32c) ? null : crc32c;
}
/**
* Returns the CRC32C checksum of blob's data as described in RFC 4960, Appendix B; decoded to
* string.
*
* @see Hashes and ETags:
* Best Practices
*/
public String getCrc32cToHexString() {
if (crc32c == null) {
return null;
}
byte[] decodeCrc32c = BaseEncoding.base64().decode(crc32c);
StringBuilder stringBuilder = new StringBuilder();
for (byte b : decodeCrc32c) {
stringBuilder.append(String.format("%02x", b & 0xff));
}
return stringBuilder.toString();
}
/** Returns the blob's media download link. */
public String getMediaLink() {
return mediaLink;
}
/** Returns blob's user provided metadata. */
@Nullable
public Map<@NonNull String, @Nullable String> getMetadata() {
return metadata == null || Data.isNull(metadata) ? null : Collections.unmodifiableMap(metadata);
}
/** Returns blob's data generation. Used for blob versioning. */
public Long getGeneration() {
return getBlobId().getGeneration();
}
/**
* Returns blob's metageneration. Used for preconditions and for detecting changes in metadata. A
* metageneration number is only meaningful in the context of a particular generation of a
* particular blob.
*/
public Long getMetageneration() {
return metageneration;
}
/**
* Returns the deletion time of the blob expressed as the number of milliseconds since the Unix
* epoch.
*
* @deprecated Use {@link #getDeleteTimeOffsetDateTime()}
*/
@Deprecated
public Long getDeleteTime() {
return millisOffsetDateTimeCodec.decode(deleteTime);
}
/** Returns the deletion time of the blob. */
public OffsetDateTime getDeleteTimeOffsetDateTime() {
return deleteTime;
}
/**
* Returns the last modification time of the blob's metadata expressed as the number of
* milliseconds since the Unix epoch.
*
* @deprecated Use {@link #getUpdateTimeOffsetDateTime()}
*/
@Deprecated
public Long getUpdateTime() {
return millisOffsetDateTimeCodec.decode(updateTime);
}
/** Returns the last modification time of the blob's metadata. */
public OffsetDateTime getUpdateTimeOffsetDateTime() {
return updateTime;
}
/**
* Returns the creation time of the blob expressed as the number of milliseconds since the Unix
* epoch.
*
* @deprecated Use {@link #getCreateTimeOffsetDateTime()}
*/
@Deprecated
public Long getCreateTime() {
return millisOffsetDateTimeCodec.decode(createTime);
}
/** Returns the creation time of the blob. */
public OffsetDateTime getCreateTimeOffsetDateTime() {
return createTime;
}
/**
* Returns the custom time specified by the user for an object.
*
* @deprecated Use {@link #getCustomTimeOffsetDateTime()}
*/
@Deprecated
public Long getCustomTime() {
return millisOffsetDateTimeCodec.decode(customTime);
}
/** Returns the custom time specified by the user for an object. */
public OffsetDateTime getCustomTimeOffsetDateTime() {
return customTime;
}
/**
* Returns {@code true} if the current blob represents a directory. This can only happen if the
* blob is returned by {@link Storage#list(String, Storage.BlobListOption...)} when the {@link
* Storage.BlobListOption#currentDirectory()} option is used. When this is the case only {@link
* #getBlobId()} and {@link #getSize()} are set for the current blob: {@link BlobId#getName()}
* ends with the '/' character, {@link BlobId#getGeneration()} returns {@code null} and {@link
* #getSize()} is {@code 0}.
*/
public boolean isDirectory() {
return isDirectory;
}
/**
* Returns information on the customer-supplied encryption key, if the blob is encrypted using
* such a key.
*/
public CustomerEncryption getCustomerEncryption() {
return customerEncryption;
}
/** Returns the storage class of the blob. */
public StorageClass getStorageClass() {
return storageClass;
}
/**
* Returns the time that the object's storage class was last changed or the time of the object
* creation.
*
* @deprecated Use {@link #getTimeStorageClassUpdatedOffsetDateTime()}
*/
@Deprecated
public Long getTimeStorageClassUpdated() {
return millisOffsetDateTimeCodec.decode(timeStorageClassUpdated);
}
/**
* Returns the time that the object's storage class was last changed or the time of the object
* creation.
*/
public OffsetDateTime getTimeStorageClassUpdatedOffsetDateTime() {
return timeStorageClassUpdated;
}
/** Returns the Cloud KMS key used to encrypt the blob, if any. */
public String getKmsKeyName() {
return kmsKeyName;
}
/**
* Returns a {@code Boolean} with either {@code true}, {@code null} and in certain cases {@code
* false}.
*
* Case 1: {@code true} the field {@link
* com.google.cloud.storage.Storage.BlobField#EVENT_BASED_HOLD} is selected in a {@link
* Storage#get(BlobId, Storage.BlobGetOption...)} and event-based hold for the blob is enabled.
*
*
Case 2.1: {@code null} the field {@link
* com.google.cloud.storage.Storage.BlobField#EVENT_BASED_HOLD} is selected in a {@link
* Storage#get(BlobId, Storage.BlobGetOption...)}, but event-based hold for the blob is not
* enabled. This case can be considered implicitly {@code false}.
*
*
Case 2.2: {@code null} the field {@link
* com.google.cloud.storage.Storage.BlobField#EVENT_BASED_HOLD} is not selected in a {@link
* Storage#get(BlobId, Storage.BlobGetOption...)}, and the state for this field is unknown.
*
*
Case 3: {@code false} event-based hold is explicitly set to false using in a {@link
* Builder#setEventBasedHold(Boolean)} client side for a follow-up request e.g. {@link
* Storage#update(BlobInfo, Storage.BlobTargetOption...)} in which case the value of event-based
* hold will remain {@code false} for the given instance.
*/
@BetaApi
public Boolean getEventBasedHold() {
return Data.isNull(eventBasedHold) ? null : eventBasedHold;
}
/**
* Returns a {@code Boolean} with either {@code true}, {@code null} and in certain cases {@code
* false}.
*
*
Case 1: {@code true} the field {@link
* com.google.cloud.storage.Storage.BlobField#TEMPORARY_HOLD} is selected in a {@link
* Storage#get(BlobId, Storage.BlobGetOption...)} and temporary hold for the blob is enabled.
*
*
Case 2.1: {@code null} the field {@link
* com.google.cloud.storage.Storage.BlobField#TEMPORARY_HOLD} is selected in a {@link
* Storage#get(BlobId, Storage.BlobGetOption...)}, but temporary hold for the blob is not enabled.
* This case can be considered implicitly {@code false}.
*
*
Case 2.2: {@code null} the field {@link
* com.google.cloud.storage.Storage.BlobField#TEMPORARY_HOLD} is not selected in a {@link
* Storage#get(BlobId, Storage.BlobGetOption...)}, and the state for this field is unknown.
*
*
Case 3: {@code false} event-based hold is explicitly set to false using in a {@link
* Builder#setEventBasedHold(Boolean)} client side for a follow-up request e.g. {@link
* Storage#update(BlobInfo, Storage.BlobTargetOption...)} in which case the value of temporary
* hold will remain {@code false} for the given instance.
*/
@BetaApi
public Boolean getTemporaryHold() {
return Data.isNull(temporaryHold) ? null : temporaryHold;
}
/**
* Returns the retention expiration time of the blob as {@code Long}, if a retention period is
* defined. If retention period is not defined this value returns {@code null}
*
* @deprecated Use {@link #getRetentionExpirationTimeOffsetDateTime()}
*/
@BetaApi
@Deprecated
public Long getRetentionExpirationTime() {
return Data.isNull(retentionExpirationTime)
? null
: millisOffsetDateTimeCodec.decode(retentionExpirationTime);
}
/**
* Returns the retention expiration time of the blob, if a retention period is defined. If
* retention period is not defined this value returns {@code null}
*/
@BetaApi
public OffsetDateTime getRetentionExpirationTimeOffsetDateTime() {
return retentionExpirationTime;
}
/** Returns the object's Retention policy. */
public Retention getRetention() {
return retention;
}
/** Returns a builder for the current blob. */
public Builder toBuilder() {
return new BuilderImpl(this);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("bucket", getBucket())
.add("name", getName())
.add("generation", getGeneration())
.add("size", getSize())
.add("content-type", getContentType())
.add("metadata", getMetadata())
.toString();
}
@Override
public int hashCode() {
return Objects.hash(
blobId,
generatedId,
selfLink,
cacheControl,
acl,
owner,
size,
etag,
md5,
crc32c,
customTime,
mediaLink,
metadata,
metageneration,
deleteTime,
updateTime,
createTime,
contentType,
contentEncoding,
contentDisposition,
contentLanguage,
storageClass,
timeStorageClassUpdated,
componentCount,
isDirectory,
customerEncryption,
kmsKeyName,
eventBasedHold,
temporaryHold,
retention,
retentionExpirationTime);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BlobInfo)) {
return false;
}
BlobInfo blobInfo = (BlobInfo) o;
return isDirectory == blobInfo.isDirectory
&& Objects.equals(blobId, blobInfo.blobId)
&& Objects.equals(generatedId, blobInfo.generatedId)
&& Objects.equals(selfLink, blobInfo.selfLink)
&& Objects.equals(cacheControl, blobInfo.cacheControl)
&& Objects.equals(acl, blobInfo.acl)
&& Objects.equals(owner, blobInfo.owner)
&& Objects.equals(size, blobInfo.size)
&& Objects.equals(etag, blobInfo.etag)
&& Objects.equals(md5, blobInfo.md5)
&& Objects.equals(crc32c, blobInfo.crc32c)
&& Objects.equals(customTime, blobInfo.customTime)
&& Objects.equals(mediaLink, blobInfo.mediaLink)
&& Objects.equals(metadata, blobInfo.metadata)
&& Objects.equals(metageneration, blobInfo.metageneration)
&& Objects.equals(deleteTime, blobInfo.deleteTime)
&& Objects.equals(updateTime, blobInfo.updateTime)
&& Objects.equals(createTime, blobInfo.createTime)
&& Objects.equals(contentType, blobInfo.contentType)
&& Objects.equals(contentEncoding, blobInfo.contentEncoding)
&& Objects.equals(contentDisposition, blobInfo.contentDisposition)
&& Objects.equals(contentLanguage, blobInfo.contentLanguage)
&& Objects.equals(storageClass, blobInfo.storageClass)
&& Objects.equals(timeStorageClassUpdated, blobInfo.timeStorageClassUpdated)
&& Objects.equals(componentCount, blobInfo.componentCount)
&& Objects.equals(customerEncryption, blobInfo.customerEncryption)
&& Objects.equals(kmsKeyName, blobInfo.kmsKeyName)
&& Objects.equals(eventBasedHold, blobInfo.eventBasedHold)
&& Objects.equals(temporaryHold, blobInfo.temporaryHold)
&& Objects.equals(retentionExpirationTime, blobInfo.retentionExpirationTime)
&& Objects.equals(retention, blobInfo.retention);
}
ImmutableSet getModifiedFields() {
return modifiedFields;
}
/**
* Attach this instance to an instance of {@link Storage} thereby allowing RPCs to be performed
* using the methods from the resulting {@link Blob}
*/
Blob asBlob(Storage storage) {
return new Blob(storage, new BuilderImpl(this));
}
/** Returns a {@code BlobInfo} builder where blob identity is set using the provided values. */
public static Builder newBuilder(BucketInfo bucketInfo, String name) {
return newBuilder(bucketInfo.getName(), name);
}
/** Returns a {@code BlobInfo} builder where blob identity is set using the provided values. */
public static Builder newBuilder(String bucket, String name) {
return newBuilder(BlobId.of(bucket, name));
}
/** Returns a {@code BlobInfo} builder where blob identity is set using the provided values. */
public static Builder newBuilder(BucketInfo bucketInfo, String name, Long generation) {
return newBuilder(bucketInfo.getName(), name, generation);
}
/** Returns a {@code BlobInfo} builder where blob identity is set using the provided values. */
public static Builder newBuilder(String bucket, String name, Long generation) {
return newBuilder(BlobId.of(bucket, name, generation));
}
/** Returns a {@code BlobInfo} builder where blob identity is set using the provided value. */
public static Builder newBuilder(BlobId blobId) {
return new BuilderImpl(blobId);
}
}